feat(web): add swc compiler option for webpack executor (#8114)
This commit is contained in:
parent
3bedfd8039
commit
f8c394af46
@ -63,6 +63,16 @@ Type: `boolean`
|
||||
|
||||
Use class components instead of functional component.
|
||||
|
||||
### compiler
|
||||
|
||||
Default: `babel`
|
||||
|
||||
Type: `string`
|
||||
|
||||
Possible values: `babel`, `swc`
|
||||
|
||||
The compiler to use
|
||||
|
||||
### directory
|
||||
|
||||
Alias(es): dir
|
||||
|
||||
@ -63,6 +63,16 @@ Type: `boolean`
|
||||
|
||||
Use a separate bundle containing code used across multiple bundles.
|
||||
|
||||
### compiler
|
||||
|
||||
Default: `babel`
|
||||
|
||||
Type: `string`
|
||||
|
||||
Possible values: `babel`, `swc`
|
||||
|
||||
The compiler to use
|
||||
|
||||
### crossOrigin
|
||||
|
||||
Type: `string`
|
||||
|
||||
@ -41,6 +41,16 @@ Type: `boolean`
|
||||
|
||||
Use babel instead ts-jest
|
||||
|
||||
### compiler
|
||||
|
||||
Default: `babel`
|
||||
|
||||
Type: `string`
|
||||
|
||||
Possible values: `babel`, `swc`
|
||||
|
||||
The compiler to use
|
||||
|
||||
### directory
|
||||
|
||||
Type: `string`
|
||||
|
||||
@ -63,6 +63,16 @@ Type: `boolean`
|
||||
|
||||
Use class components instead of functional component.
|
||||
|
||||
### compiler
|
||||
|
||||
Default: `babel`
|
||||
|
||||
Type: `string`
|
||||
|
||||
Possible values: `babel`, `swc`
|
||||
|
||||
The compiler to use
|
||||
|
||||
### directory
|
||||
|
||||
Alias(es): dir
|
||||
|
||||
@ -63,6 +63,16 @@ Type: `boolean`
|
||||
|
||||
Use a separate bundle containing code used across multiple bundles.
|
||||
|
||||
### compiler
|
||||
|
||||
Default: `babel`
|
||||
|
||||
Type: `string`
|
||||
|
||||
Possible values: `babel`, `swc`
|
||||
|
||||
The compiler to use
|
||||
|
||||
### crossOrigin
|
||||
|
||||
Type: `string`
|
||||
|
||||
@ -41,6 +41,16 @@ Type: `boolean`
|
||||
|
||||
Use babel instead ts-jest
|
||||
|
||||
### compiler
|
||||
|
||||
Default: `babel`
|
||||
|
||||
Type: `string`
|
||||
|
||||
Possible values: `babel`, `swc`
|
||||
|
||||
The compiler to use
|
||||
|
||||
### directory
|
||||
|
||||
Type: `string`
|
||||
|
||||
@ -63,6 +63,16 @@ Type: `boolean`
|
||||
|
||||
Use class components instead of functional component.
|
||||
|
||||
### compiler
|
||||
|
||||
Default: `babel`
|
||||
|
||||
Type: `string`
|
||||
|
||||
Possible values: `babel`, `swc`
|
||||
|
||||
The compiler to use
|
||||
|
||||
### directory
|
||||
|
||||
Alias(es): dir
|
||||
|
||||
@ -63,6 +63,16 @@ Type: `boolean`
|
||||
|
||||
Use a separate bundle containing code used across multiple bundles.
|
||||
|
||||
### compiler
|
||||
|
||||
Default: `babel`
|
||||
|
||||
Type: `string`
|
||||
|
||||
Possible values: `babel`, `swc`
|
||||
|
||||
The compiler to use
|
||||
|
||||
### crossOrigin
|
||||
|
||||
Type: `string`
|
||||
|
||||
@ -41,6 +41,16 @@ Type: `boolean`
|
||||
|
||||
Use babel instead ts-jest
|
||||
|
||||
### compiler
|
||||
|
||||
Default: `babel`
|
||||
|
||||
Type: `string`
|
||||
|
||||
Possible values: `babel`, `swc`
|
||||
|
||||
The compiler to use
|
||||
|
||||
### directory
|
||||
|
||||
Type: `string`
|
||||
|
||||
@ -73,7 +73,7 @@ describe('Web Components Applications', () => {
|
||||
const appName = uniq('app');
|
||||
const libName = uniq('lib');
|
||||
|
||||
runCLI(`generate @nrwl/web:app ${appName} --no-interactive`);
|
||||
runCLI(`generate @nrwl/web:app ${appName} --no-interactive --compiler swc`);
|
||||
runCLI(
|
||||
`generate @nrwl/react:lib ${libName} --buildable --no-interactive --compiler swc`
|
||||
);
|
||||
|
||||
@ -277,6 +277,7 @@ Object {
|
||||
expect(targetConfig.build.executor).toEqual('@nrwl/web:webpack');
|
||||
expect(targetConfig.build.outputs).toEqual(['{options.outputPath}']);
|
||||
expect(targetConfig.build.options).toEqual({
|
||||
compiler: 'babel',
|
||||
assets: ['apps/my-app/src/favicon.ico', 'apps/my-app/src/assets'],
|
||||
index: 'apps/my-app/src/index.html',
|
||||
main: 'apps/my-app/src/main.tsx',
|
||||
@ -735,4 +736,19 @@ Object {
|
||||
).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('--compiler', () => {
|
||||
it('should install swc packages if --compiler=swc', async () => {
|
||||
await applicationGenerator(appTree, {
|
||||
...schema,
|
||||
compiler: 'swc',
|
||||
});
|
||||
const packageJson = readJson(appTree, '/package.json');
|
||||
|
||||
expect(packageJson.devDependencies).toMatchObject({
|
||||
'@swc/core': expect.any(String),
|
||||
'swc-loader': expect.any(String),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import {
|
||||
extraEslintDependencies,
|
||||
createReactEslintJson,
|
||||
extraEslintDependencies,
|
||||
} from '../../utils/lint';
|
||||
import { NormalizedSchema, Schema } from './schema';
|
||||
import { createApplicationFiles } from './lib/create-application-files';
|
||||
@ -24,6 +24,8 @@ import {
|
||||
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
|
||||
import reactInitGenerator from '../init/init';
|
||||
import { lintProjectGenerator } from '@nrwl/linter';
|
||||
import { swcCoreVersion } from '@nrwl/js/src/utils/versions';
|
||||
import { swcLoaderVersion } from '@nrwl/web/src/utils/versions';
|
||||
|
||||
async function addLinting(host: Tree, options: NormalizedSchema) {
|
||||
const tasks: GeneratorCallback[] = [];
|
||||
@ -52,7 +54,12 @@ async function addLinting(host: Tree, options: NormalizedSchema) {
|
||||
const installTask = await addDependenciesToPackageJson(
|
||||
host,
|
||||
extraEslintDependencies.dependencies,
|
||||
extraEslintDependencies.devDependencies
|
||||
{
|
||||
...extraEslintDependencies.devDependencies,
|
||||
...(options.compiler === 'swc'
|
||||
? { '@swc/core': swcCoreVersion, 'swc-loader': swcLoaderVersion }
|
||||
: {}),
|
||||
}
|
||||
);
|
||||
tasks.push(installTask);
|
||||
|
||||
|
||||
@ -40,6 +40,7 @@ function createBuildTarget(options: NormalizedSchema): TargetConfiguration {
|
||||
outputs: ['{options.outputPath}'],
|
||||
defaultConfiguration: 'production',
|
||||
options: {
|
||||
compiler: options.compiler ?? 'babel',
|
||||
outputPath: joinPathFragments('dist', options.appProjectRoot),
|
||||
index: joinPathFragments(options.appProjectRoot, 'src/index.html'),
|
||||
baseHref: '/',
|
||||
|
||||
@ -20,6 +20,7 @@ export interface Schema {
|
||||
strict?: boolean;
|
||||
setParserOptionsProject?: boolean;
|
||||
standaloneConfig?: boolean;
|
||||
compiler?: 'babel' | 'swc';
|
||||
}
|
||||
|
||||
export interface NormalizedSchema extends Schema {
|
||||
|
||||
@ -152,6 +152,12 @@
|
||||
"standaloneConfig": {
|
||||
"description": "Split the project configuration into <projectRoot>/project.json rather than including it inside workspace.json",
|
||||
"type": "boolean"
|
||||
},
|
||||
"compiler": {
|
||||
"type": "string",
|
||||
"description": "The compiler to use",
|
||||
"enum": ["babel", "swc"],
|
||||
"default": "babel"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
|
||||
@ -16,6 +16,12 @@
|
||||
"type": "string",
|
||||
"description": "The name of the Typescript configuration file."
|
||||
},
|
||||
"compiler": {
|
||||
"type": "string",
|
||||
"description": "The compiler to use",
|
||||
"enum": ["babel", "swc"],
|
||||
"default": "babel"
|
||||
},
|
||||
"outputPath": {
|
||||
"type": "string",
|
||||
"description": "The output path of the generated files."
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { ExecutorContext } from '@nrwl/devkit';
|
||||
import { ExecutorContext, logger } from '@nrwl/devkit';
|
||||
import type { Configuration, Stats } from 'webpack';
|
||||
import { from, of } from 'rxjs';
|
||||
import { bufferCount, mergeScan, switchMap, tap } from 'rxjs/operators';
|
||||
@ -132,6 +132,18 @@ export async function* run(
|
||||
|
||||
const metadata = context.workspace.projects[context.projectName];
|
||||
|
||||
if (options.compiler === 'swc') {
|
||||
try {
|
||||
require.resolve('swc-loader');
|
||||
require.resolve('@swc/core');
|
||||
} catch {
|
||||
logger.error(
|
||||
`Missing SWC dependencies: @swc/core, swc-loader. Make sure you install them first.`
|
||||
);
|
||||
return { success: false };
|
||||
}
|
||||
}
|
||||
|
||||
if (!options.buildLibsFromSource && context.targetName) {
|
||||
const { dependencies } = calculateProjectDependencies(
|
||||
readCachedProjectGraph(),
|
||||
|
||||
@ -257,6 +257,7 @@ describe('app', () => {
|
||||
expect(architectConfig.build.builder).toEqual('@nrwl/web:webpack');
|
||||
expect(architectConfig.build.outputs).toEqual(['{options.outputPath}']);
|
||||
expect(architectConfig.build.options).toEqual({
|
||||
compiler: 'babel',
|
||||
assets: ['apps/my-app/src/favicon.ico', 'apps/my-app/src/assets'],
|
||||
index: 'apps/my-app/src/index.html',
|
||||
baseHref: '/',
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import {
|
||||
addDependenciesToPackageJson,
|
||||
addProjectConfiguration,
|
||||
convertNxGenerator,
|
||||
formatFiles,
|
||||
@ -25,6 +26,8 @@ import { jestProjectGenerator } from '@nrwl/jest';
|
||||
|
||||
import { WebWebpackExecutorOptions } from '../../executors/webpack/webpack.impl';
|
||||
import { Schema } from './schema';
|
||||
import { swcCoreVersion } from '@nrwl/js/src/utils/versions';
|
||||
import { swcLoaderVersion } from '../../utils/versions';
|
||||
|
||||
interface NormalizedSchema extends Schema {
|
||||
projectName: string;
|
||||
@ -52,6 +55,7 @@ function addBuildTarget(
|
||||
): ProjectConfiguration {
|
||||
const buildOptions: WebWebpackExecutorOptions = {
|
||||
outputPath: joinPathFragments('dist', options.appProjectRoot),
|
||||
compiler: options.compiler ?? 'babel',
|
||||
index: joinPathFragments(options.appProjectRoot, 'src/index.html'),
|
||||
baseHref: '/',
|
||||
main: joinPathFragments(options.appProjectRoot, 'src/main.ts'),
|
||||
@ -222,6 +226,15 @@ export async function applicationGenerator(host: Tree, schema: Schema) {
|
||||
tasks.push(jestTask);
|
||||
}
|
||||
|
||||
if (options.compiler === 'swc') {
|
||||
const installTask = await addDependenciesToPackageJson(
|
||||
host,
|
||||
{},
|
||||
{ '@swc/core': swcCoreVersion, 'swc-loader': swcLoaderVersion }
|
||||
);
|
||||
tasks.push(installTask);
|
||||
}
|
||||
|
||||
setDefaults(host, options);
|
||||
|
||||
if (!schema.skipFormat) {
|
||||
|
||||
@ -4,6 +4,7 @@ export interface Schema {
|
||||
name: string;
|
||||
prefix?: string;
|
||||
style?: string;
|
||||
compiler?: 'babel' | 'swc';
|
||||
skipFormat?: boolean;
|
||||
directory?: string;
|
||||
tags?: string;
|
||||
|
||||
@ -43,6 +43,12 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"compiler": {
|
||||
"type": "string",
|
||||
"description": "The compiler to use",
|
||||
"enum": ["babel", "swc"],
|
||||
"default": "babel"
|
||||
},
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
|
||||
@ -72,7 +72,7 @@ export function getBaseWebpackPartial(
|
||||
fullySpecified: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
options.compiler === 'babel' && {
|
||||
test: /\.([jt])sx?$/,
|
||||
loader: join(__dirname, 'web-babel-loader'),
|
||||
exclude: /node_modules/,
|
||||
@ -87,7 +87,27 @@ export function getBaseWebpackPartial(
|
||||
cacheCompression: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
options.compiler === 'swc' && {
|
||||
test: /\.([jt])sx?$/,
|
||||
loader: require.resolve('swc-loader'),
|
||||
exclude: /node_modules/,
|
||||
options: {
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: 'typescript',
|
||||
decorators: true,
|
||||
tsx: true,
|
||||
},
|
||||
transform: {
|
||||
react: {
|
||||
runtime: 'automatic',
|
||||
},
|
||||
},
|
||||
loose: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
].filter(Boolean),
|
||||
},
|
||||
resolve: {
|
||||
extensions,
|
||||
@ -123,7 +143,7 @@ export function getBaseWebpackPartial(
|
||||
},
|
||||
};
|
||||
|
||||
if (isScriptOptimizeOn) {
|
||||
if (options.compiler !== 'swc' && isScriptOptimizeOn) {
|
||||
webpackConfig.optimization = {
|
||||
sideEffects: false,
|
||||
minimizer: [
|
||||
|
||||
@ -10,6 +10,7 @@ describe('normalizeBuildOptions', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
testOptions = {
|
||||
compiler: 'babel',
|
||||
main: 'apps/nodeapp/src/main.ts',
|
||||
tsConfig: 'apps/nodeapp/tsconfig.app.json',
|
||||
outputPath: 'dist/apps/nodeapp',
|
||||
|
||||
@ -9,6 +9,7 @@ export interface OptimizationOptions {
|
||||
export interface BuildBuilderOptions {
|
||||
main: string;
|
||||
outputPath: string;
|
||||
compiler: 'babel' | 'swc';
|
||||
tsConfig: string;
|
||||
watch?: boolean;
|
||||
sourceMap?: boolean | 'hidden';
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
export const nxVersion = '*';
|
||||
|
||||
export const swcLoaderVersion = '0.1.15';
|
||||
export const sassVersion = '1.43.2';
|
||||
|
||||
@ -8,13 +8,11 @@ import { ExtraEntryPoint, WebpackConfigOptions } from '../../shared-models';
|
||||
import { BuildBrowserFeatures } from '../build-browser-features';
|
||||
import { getOutputHashFormat } from '../../hash-format';
|
||||
import { normalizeExtraEntryPoints } from '../../normalize';
|
||||
import CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
||||
import { findAllNodeModules, findUp } from '../../fs';
|
||||
import CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
|
||||
|
||||
const ProgressPlugin = require('webpack/lib/ProgressPlugin');
|
||||
const TerserPlugin = require('terser-webpack-plugin');
|
||||
|
||||
// tslint:disable-next-line:no-big-function
|
||||
export function getCommonConfig(wco: WebpackConfigOptions): Configuration {
|
||||
const { ContextReplacementPlugin } = webpack;
|
||||
|
||||
@ -208,52 +206,6 @@ export function getCommonConfig(wco: WebpackConfigOptions): Configuration {
|
||||
);
|
||||
}
|
||||
|
||||
if (scriptsOptimization) {
|
||||
// TODO: Investigate why this fails for some packages: wco.supportES2015 ? 6 : 5;
|
||||
const terserEcma = 5;
|
||||
|
||||
const terserOptions = {
|
||||
warnings: !!buildOptions.verbose,
|
||||
safari10: true,
|
||||
output: {
|
||||
ecma: terserEcma,
|
||||
comments: false,
|
||||
webkit: true,
|
||||
},
|
||||
// On server, we don't want to compress anything. We still set the ngDevMode = false for it
|
||||
// to remove dev code, and ngI18nClosureMode to remove Closure compiler i18n code
|
||||
compress: {
|
||||
ecma: terserEcma,
|
||||
// TODO(jack): Investigate options to enable further optimizations
|
||||
// pure_getters: true,
|
||||
// PURE comments work best with 3 passes.
|
||||
// See https://github.com/webpack/webpack/issues/2899#issuecomment-317425926.
|
||||
// passes: 3,
|
||||
},
|
||||
mangle: true,
|
||||
};
|
||||
|
||||
const es5TerserOptions = {
|
||||
...terserOptions,
|
||||
compress: {
|
||||
...terserOptions.compress,
|
||||
ecma: 5,
|
||||
},
|
||||
output: {
|
||||
...terserOptions.output,
|
||||
ecma: 5,
|
||||
},
|
||||
};
|
||||
|
||||
extraMinimizers.push(
|
||||
new TerserPlugin({ terserOptions }),
|
||||
|
||||
// Script bundles are fully optimized here in one step since they are never downleveled.
|
||||
// They are shared between ES2015 & ES5 outputs so must support ES5.
|
||||
new TerserPlugin({ terserOptions: es5TerserOptions })
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
mode:
|
||||
scriptsOptimization || stylesOptimization ? 'production' : 'development',
|
||||
|
||||
@ -90,7 +90,10 @@ const IGNORE_MATCHES = {
|
||||
'@angular-devkit/architect',
|
||||
],
|
||||
web: [
|
||||
'@swc/core', // we don't want to bloat the install of @nrwl/web by including @swc/core as a dependency.
|
||||
// we don't want to bloat the install of @nrwl/web by including @swc/core and swc-loader as a dependency.
|
||||
'@swc/core',
|
||||
'swc-loader',
|
||||
|
||||
'fibers',
|
||||
'node-sass',
|
||||
],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user