feat(js): do not generate root babel.config.json for babel projects (#17289)

This commit is contained in:
Jack Hsu 2023-06-06 15:45:33 -04:00 committed by GitHub
parent 5c7db06508
commit 08339ee49f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
59 changed files with 476 additions and 519 deletions

View File

@ -50,10 +50,16 @@
"type": "string", "type": "string",
"description": "Frontend project that needs to access this application. This sets up proxy configuration." "description": "Frontend project that needs to access this application. This sets up proxy configuration."
}, },
"swcJest": {
"type": "boolean",
"description": "Use `@swc/jest` instead `ts-jest` for faster test compilation.",
"default": false
},
"babelJest": { "babelJest": {
"type": "boolean", "type": "boolean",
"description": "Use `babel` instead `ts-jest`.", "description": "Use `babel` instead `ts-jest`.",
"default": false "default": false,
"x-deprecated": "Use --swcJest instead for faster compilation"
}, },
"pascalCaseFiles": { "pascalCaseFiles": {
"type": "boolean", "type": "boolean",

View File

@ -54,10 +54,16 @@
"description": "Frontend project that needs to access this application. This sets up proxy configuration.", "description": "Frontend project that needs to access this application. This sets up proxy configuration.",
"x-priority": "important" "x-priority": "important"
}, },
"swcJest": {
"type": "boolean",
"description": "Use `@swc/jest` instead `ts-jest` for faster test compilation.",
"default": false
},
"babelJest": { "babelJest": {
"type": "boolean", "type": "boolean",
"description": "Use `babel` instead of `ts-jest`.", "description": "Use `babel` instead `ts-jest`.",
"default": false "default": false,
"x-deprecated": "Use --swcJest instead for faster compilation"
}, },
"pascalCaseFiles": { "pascalCaseFiles": {
"type": "boolean", "type": "boolean",

View File

@ -92,8 +92,8 @@
"default": false, "default": false,
"x-priority": "internal" "x-priority": "internal"
}, },
"skipWorkspaceJson": { "skipNxJson": {
"description": "Skip updating `workspace.json` with default options based on values provided to this app (e.g. babel, style).", "description": "Skip updating `nx.json` with default options based on values provided to this app.",
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"x-priority": "internal" "x-priority": "internal"

View File

@ -73,7 +73,7 @@
"default": false, "default": false,
"x-priority": "internal" "x-priority": "internal"
}, },
"skipWorkspaceJson": { "skipNxJson": {
"description": "Skip updating workspace.json with default options based on values provided to this app (e.g. babel, style).", "description": "Skip updating workspace.json with default options based on values provided to this app (e.g. babel, style).",
"type": "boolean", "type": "boolean",
"default": false, "default": false,

View File

@ -31,11 +31,6 @@
"type": "boolean", "type": "boolean",
"default": false "default": false
}, },
"skipBabelConfig": {
"description": "Do not generate a root babel.config.json (if babel is not needed).",
"type": "boolean",
"default": false
},
"skipHelperLibs": { "skipHelperLibs": {
"description": "Do not install tslib.", "description": "Do not install tslib.",
"type": "boolean", "type": "boolean",

View File

@ -78,7 +78,7 @@
"default": false, "default": false,
"x-priority": "internal" "x-priority": "internal"
}, },
"skipWorkspaceJson": { "skipNxJson": {
"description": "Skip updating workspace.json with default options based on values provided to this app (e.g. babel, style).", "description": "Skip updating workspace.json with default options based on values provided to this app (e.g. babel, style).",
"type": "boolean", "type": "boolean",
"default": false, "default": false,

View File

@ -43,8 +43,8 @@
"compiler": { "compiler": {
"type": "string", "type": "string",
"description": "The compiler to use", "description": "The compiler to use",
"enum": ["babel", "swc"], "enum": ["swc", "babel"],
"default": "babel", "default": "swc",
"x-priority": "important" "x-priority": "important"
}, },
"bundler": { "bundler": {

View File

@ -37,11 +37,6 @@
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"x-priority": "internal" "x-priority": "internal"
},
"skipBabelConfig": {
"description": "Do not generate a root babel.config.json (if babel is not needed).",
"type": "boolean",
"default": false
} }
}, },
"required": [], "required": [],

View File

@ -21,7 +21,7 @@
"type": "string", "type": "string",
"enum": ["babel", "swc", "tsc"], "enum": ["babel", "swc", "tsc"],
"description": "The compiler to use to build source.", "description": "The compiler to use to build source.",
"default": "babel" "default": "swc"
}, },
"main": { "main": {
"type": "string", "type": "string",

View File

@ -7,7 +7,6 @@ import {
killPorts, killPorts,
newProject, newProject,
readFile, readFile,
removeFile,
rmDist, rmDist,
runCLI, runCLI,
runCLIAsync, runCLIAsync,
@ -100,10 +99,10 @@ describe('Web Components Applications', () => {
checkFilesExist(`dist/libs/${libName}/_should_keep.txt`); checkFilesExist(`dist/libs/${libName}/_should_keep.txt`);
}, 120000); }, 120000);
it('should emit decorator metadata when it is enabled in tsconfig', async () => { it('should emit decorator metadata when --compiler=babel and it is enabled in tsconfig', async () => {
const appName = uniq('app'); const appName = uniq('app');
runCLI( runCLI(
`generate @nx/web:app ${appName} --bundler=webpack --no-interactive` `generate @nx/web:app ${appName} --bundler=webpack --compiler=babel --no-interactive`
); );
updateFile(`apps/${appName}/src/app/app.element.ts`, (content) => { updateFile(`apps/${appName}/src/app/app.element.ts`, (content) => {
@ -155,6 +154,48 @@ describe('Web Components Applications', () => {
); );
}, 120000); }, 120000);
it('should emit decorator metadata when using --compiler=swc', async () => {
const appName = uniq('app');
runCLI(
`generate @nx/web:app ${appName} --bundler=webpack --compiler=swc --no-interactive`
);
updateFile(`apps/${appName}/src/app/app.element.ts`, (content) => {
const newContent = `${content}
function enumerable(value: boolean) {
return function (
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
descriptor.enumerable = value;
};
}
function sealed(target: any) {
return target;
}
@sealed
class Foo {
@enumerable(false) bar() {}
}
`;
return newContent;
});
updateFile(`apps/${appName}/src/app/app.element.ts`, (content) => {
const newContent = `${content}
// bust babel and nx cache
`;
return newContent;
});
runCLI(`build ${appName} --outputHashing none`);
expect(readFile(`dist/apps/${appName}/main.js`)).toMatch(
/Foo=__decorate\(\[sealed\],Foo\)/
);
}, 120000);
it('should support custom webpackConfig option', async () => { it('should support custom webpackConfig option', async () => {
const appName = uniq('app'); const appName = uniq('app');
runCLI( runCLI(
@ -257,7 +298,7 @@ describe('CLI - Environment Variables', () => {
`; `;
runCLI( runCLI(
`generate @nx/web:app ${appName} --bundler=webpack --no-interactive` `generate @nx/web:app ${appName} --bundler=webpack --no-interactive --compiler=babel`
); );
const content = readFile(main); const content = readFile(main);
@ -282,7 +323,7 @@ describe('CLI - Environment Variables', () => {
const newCode2 = `const envVars = [process.env.NODE_ENV, process.env.NX_BUILD, process.env.NX_API, process.env.NX_WS_BASE, process.env.NX_WS_ENV_LOCAL, process.env.NX_WS_LOCAL_ENV, process.env.NX_APP_BASE, process.env.NX_APP_ENV_LOCAL, process.env.NX_APP_LOCAL_ENV, process.env.NX_SHARED_ENV];`; const newCode2 = `const envVars = [process.env.NODE_ENV, process.env.NX_BUILD, process.env.NX_API, process.env.NX_WS_BASE, process.env.NX_WS_ENV_LOCAL, process.env.NX_WS_LOCAL_ENV, process.env.NX_APP_BASE, process.env.NX_APP_ENV_LOCAL, process.env.NX_APP_LOCAL_ENV, process.env.NX_SHARED_ENV];`;
runCLI( runCLI(
`generate @nx/web:app ${appName2} --bundler=webpack --no-interactive` `generate @nx/web:app ${appName2} --bundler=webpack --no-interactive --compiler=babel`
); );
const content2 = readFile(main2); const content2 = readFile(main2);
@ -401,16 +442,16 @@ describe('index.html interpolation', () => {
const srcPath = `apps/${appName}/src`; const srcPath = `apps/${appName}/src`;
const indexPath = `${srcPath}/index.html`; const indexPath = `${srcPath}/index.html`;
const indexContent = `<!DOCTYPE html> const indexContent = `<!DOCTYPE html>
<html lang="en"> <html lang='en'>
<head> <head>
<meta charset="utf-8" /> <meta charset='utf-8' />
<title>BestReactApp</title> <title>BestReactApp</title>
<base href="/" /> <base href='/' />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name='viewport' content='width=device-width, initial-scale=1' />
<link rel="icon" type="image/x-icon" href="favicon.ico" /> <link rel='icon' type='image/x-icon' href='favicon.ico' />
</head> </head>
<body> <body>
<div id="root"></div> <div id='root'></div>
<div>Nx Variable: %NX_VARIABLE%</div> <div>Nx Variable: %NX_VARIABLE%</div>
<div>Some other variable: %SOME_OTHER_VARIABLE%</div> <div>Some other variable: %SOME_OTHER_VARIABLE%</div>
<div>Deploy Url: %DEPLOY_URL%</div> <div>Deploy Url: %DEPLOY_URL%</div>

View File

@ -10,6 +10,8 @@ export interface Schema {
tags?: string; tags?: string;
linter: Linter; linter: Linter;
frontendProject?: string; frontendProject?: string;
swcJest?: boolean;
/** @deprecated use `swcJest` instead */
babelJest?: boolean; babelJest?: boolean;
js: boolean; js: boolean;
pascalCaseFiles: boolean; pascalCaseFiles: boolean;

View File

@ -50,10 +50,16 @@
"type": "string", "type": "string",
"description": "Frontend project that needs to access this application. This sets up proxy configuration." "description": "Frontend project that needs to access this application. This sets up proxy configuration."
}, },
"swcJest": {
"type": "boolean",
"description": "Use `@swc/jest` instead `ts-jest` for faster test compilation.",
"default": false
},
"babelJest": { "babelJest": {
"type": "boolean", "type": "boolean",
"description": "Use `babel` instead `ts-jest`.", "description": "Use `babel` instead `ts-jest`.",
"default": false "default": false,
"x-deprecated": "Use --swcJest instead for faster compilation"
}, },
"pascalCaseFiles": { "pascalCaseFiles": {
"type": "boolean", "type": "boolean",

View File

@ -8,7 +8,12 @@ export default {
transform: { transform: {
'^.+\\\\.[tj]sx?$': [ '^.+\\\\.[tj]sx?$': [
'@swc/jest', '@swc/jest',
{ jsc: { transform: { react: { runtime: 'automatic' } } } }, {
jsc: {
parser: { syntax: 'typescript', tsx: true },
transform: { react: { runtime: 'automatic' } },
},
},
], ],
}, },
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],

View File

@ -60,15 +60,6 @@ describe('jestProject', () => {
expect(tree.read('libs/lib1/jest.config.ts', 'utf-8')).toMatchSnapshot(); expect(tree.read('libs/lib1/jest.config.ts', 'utf-8')).toMatchSnapshot();
}); });
it('should generate files w/babel-jest', async () => {
await jestProjectGenerator(tree, {
...defaultOptions,
project: 'lib1',
babelJest: true,
} as JestProjectSchema);
expect(tree.exists('babel.config.json')).toBeTruthy();
});
it('should alter project configuration', async () => { it('should alter project configuration', async () => {
await jestProjectGenerator(tree, { await jestProjectGenerator(tree, {
...defaultOptions, ...defaultOptions,

View File

@ -21,7 +21,7 @@ export function createFiles(tree: Tree, options: NormalizedJestProjectSchema) {
transformer = '@swc/jest'; transformer = '@swc/jest';
if (options.supportTsx) { if (options.supportTsx) {
transformerOptions = transformerOptions =
"{ jsc: { transform: { react: { runtime: 'automatic' } } } }"; "{ jsc: { parser: { syntax: 'typescript', tsx: true }, transform: { react: { runtime: 'automatic' } } } }";
} }
} else { } else {
transformer = 'ts-jest'; transformer = 'ts-jest';
@ -48,15 +48,6 @@ export function createFiles(tree: Tree, options: NormalizedJestProjectSchema) {
tree.delete(join(projectConfig.root, './src/test-setup.ts')); tree.delete(join(projectConfig.root, './src/test-setup.ts'));
} }
if (options.babelJest && !tree.exists('babel.config.json')) {
tree.write(
'babel.config.json',
JSON.stringify({
babelrcRoots: ['*'],
})
);
}
if (options.js) { if (options.js) {
tree.rename( tree.rename(
join(projectConfig.root, 'jest.config.ts'), join(projectConfig.root, 'jest.config.ts'),

View File

@ -91,10 +91,6 @@ export async function projectGenerator(
}); });
tasks.push(viteTask); tasks.push(viteTask);
} }
if (options.bundler === 'rollup') {
ensureBabelRootConfigExists(tree);
}
if (options.linter !== 'none') { if (options.linter !== 'none') {
const lintCallback = await addLint(tree, options); const lintCallback = await addLint(tree, options);
tasks.push(lintCallback); tasks.push(lintCallback);
@ -583,14 +579,6 @@ function getBuildExecutor(bundler: Bundler) {
} }
} }
function ensureBabelRootConfigExists(tree: Tree) {
if (tree.exists('babel.config.json')) return;
writeJson(tree, 'babel.config.json', {
babelrcRoots: ['*'],
});
}
function getOutputPath(options: NormalizedSchema, destinationDir?: string) { function getOutputPath(options: NormalizedSchema, destinationDir?: string) {
const parts = ['dist']; const parts = ['dist'];
if (destinationDir) { if (destinationDir) {

View File

@ -1,5 +1,4 @@
import { import {
formatFiles,
joinPathFragments, joinPathFragments,
readNxJson, readNxJson,
Tree, Tree,
@ -7,6 +6,8 @@ import {
writeJson, writeJson,
} from '@nx/devkit'; } from '@nx/devkit';
/** @deprecated Do not use this function as the root babel.config.json file is no longer needed */
// TODO(jack): Remove This in Nx 17 once we don't need to support Nx 15 anymore. Currently this function is used in v15 migrations.
export function addBabelInputs(tree: Tree) { export function addBabelInputs(tree: Tree) {
const nxJson = readNxJson(tree); const nxJson = readNxJson(tree);
let globalBabelFile = ['babel.config.js', 'babel.config.json'].find((file) => let globalBabelFile = ['babel.config.js', 'babel.config.json'].find((file) =>

View File

@ -65,7 +65,6 @@ export async function nextInitGenerator(host: Tree, schema: InitSchema) {
const reactTask = await reactInitGenerator(host, { const reactTask = await reactInitGenerator(host, {
...schema, ...schema,
skipFormat: true, skipFormat: true,
skipBabelConfig: true,
}); });
tasks.push(reactTask); tasks.push(reactTask);

View File

@ -13,67 +13,6 @@ describe('next library', () => {
let mockedInstalledCypressVersion: jest.Mock< let mockedInstalledCypressVersion: jest.Mock<
ReturnType<typeof installedCypressVersion> ReturnType<typeof installedCypressVersion>
> = installedCypressVersion as never; > = installedCypressVersion as never;
it('should use "@nx/next/babel" preset in babelrc', async () => {
const baseOptions: Schema = {
name: '',
linter: Linter.EsLint,
skipFormat: false,
skipTsConfig: false,
unitTestRunner: 'jest',
style: 'css',
component: true,
};
const appTree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
await libraryGenerator(appTree, {
...baseOptions,
name: 'myLib',
});
await libraryGenerator(appTree, {
...baseOptions,
name: 'myLib2',
style: '@emotion/styled',
});
await libraryGenerator(appTree, {
...baseOptions,
name: 'myLib-styled-jsx',
style: 'styled-jsx',
});
await libraryGenerator(appTree, {
...baseOptions,
name: 'myLib3',
directory: 'myDir',
});
expect(readJson(appTree, 'libs/my-lib/.babelrc')).toEqual({
presets: ['@nx/next/babel'],
plugins: [],
});
expect(readJson(appTree, 'libs/my-lib2/.babelrc')).toEqual({
presets: [
[
'@nx/next/babel',
{
'preset-react': {
runtime: 'automatic',
importSource: '@emotion/react',
},
},
],
],
plugins: ['@emotion/babel-plugin'],
});
expect(readJson(appTree, 'libs/my-lib-styled-jsx/.babelrc')).toEqual({
presets: ['@nx/next/babel'],
plugins: [],
});
expect(readJson(appTree, 'libs/my-dir/my-lib3/.babelrc')).toEqual({
presets: ['@nx/next/babel'],
plugins: [],
});
});
it('should use @nx/next images.d.ts file', async () => { it('should use @nx/next images.d.ts file', async () => {
const baseOptions: Schema = { const baseOptions: Schema = {
name: '', name: '',

View File

@ -71,36 +71,6 @@ export async function libraryGenerator(host: Tree, rawOptions: Schema) {
); );
addTsConfigPath(host, `${options.importPath}/server`, [serverEntryPath]); addTsConfigPath(host, `${options.importPath}/server`, [serverEntryPath]);
updateJson(
host,
joinPathFragments(options.projectRoot, '.babelrc'),
(json) => {
if (options.style === '@emotion/styled') {
json.presets = [
[
'@nx/next/babel',
{
'preset-react': {
runtime: 'automatic',
importSource: '@emotion/react',
},
},
],
];
} else if (options.style === 'styled-jsx') {
// next.js doesn't require the `styled-jsx/babel' plugin as it is already
// built-into the `next/babel` preset
json.presets = ['@nx/next/babel'];
json.plugins = (json.plugins || []).filter(
(x) => x !== 'styled-jsx/babel'
);
} else {
json.presets = ['@nx/next/babel'];
}
return json;
}
);
updateJson( updateJson(
host, host,
joinPathFragments(options.projectRoot, 'tsconfig.json'), joinPathFragments(options.projectRoot, 'tsconfig.json'),

View File

@ -343,7 +343,33 @@ describe('app', () => {
}); });
}); });
describe('--babelJest', () => { describe('--swcJest', () => {
it('should use @swc/jest for jest', async () => {
await applicationGenerator(tree, {
name: 'myNodeApp',
tags: 'one,two',
swcJest: true,
} as Schema);
expect(tree.read(`my-node-app/jest.config.ts`, 'utf-8'))
.toMatchInlineSnapshot(`
"/* eslint-disable */
export default {
displayName: 'my-node-app',
preset: '../jest.preset.js',
testEnvironment: 'node',
transform: {
'^.+\\\\.[tj]s$': '@swc/jest',
},
moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../coverage/my-node-app',
};
"
`);
});
});
describe('--babelJest (deprecated)', () => {
it('should use babel for jest', async () => { it('should use babel for jest', async () => {
await applicationGenerator(tree, { await applicationGenerator(tree, {
name: 'myNodeApp', name: 'myNodeApp',
@ -368,6 +394,7 @@ describe('app', () => {
`); `);
}); });
}); });
describe('--js flag', () => { describe('--js flag', () => {
it('should generate js files instead of ts files', async () => { it('should generate js files instead of ts files', async () => {
await applicationGenerator(tree, { await applicationGenerator(tree, {

View File

@ -398,7 +398,7 @@ export async function applicationGenerator(tree: Tree, schema: Schema) {
skipSerializers: true, skipSerializers: true,
supportTsx: options.js, supportTsx: options.js,
testEnvironment: 'node', testEnvironment: 'node',
compiler: 'tsc', compiler: options.swcJest ? 'swc' : 'tsc',
skipFormat: true, skipFormat: true,
}); });
tasks.push(jestTask); tasks.push(jestTask);

View File

@ -10,6 +10,8 @@ export interface Schema {
linter?: Linter; linter?: Linter;
tags?: string; tags?: string;
frontendProject?: string; frontendProject?: string;
swcJest?: boolean;
/** @deprecated use `swcJest` instead */
babelJest?: boolean; babelJest?: boolean;
js?: boolean; js?: boolean;
pascalCaseFiles?: boolean; pascalCaseFiles?: boolean;

View File

@ -54,10 +54,16 @@
"description": "Frontend project that needs to access this application. This sets up proxy configuration.", "description": "Frontend project that needs to access this application. This sets up proxy configuration.",
"x-priority": "important" "x-priority": "important"
}, },
"swcJest": {
"type": "boolean",
"description": "Use `@swc/jest` instead `ts-jest` for faster test compilation.",
"default": false
},
"babelJest": { "babelJest": {
"type": "boolean", "type": "boolean",
"description": "Use `babel` instead of `ts-jest`.", "description": "Use `babel` instead `ts-jest`.",
"default": false "default": false,
"x-deprecated": "Use --swcJest instead for faster compilation"
}, },
"pascalCaseFiles": { "pascalCaseFiles": {
"type": "boolean", "type": "boolean",

View File

@ -35,47 +35,4 @@ describe('init', () => {
expect(content).toMatch(/# React Native/); expect(content).toMatch(/# React Native/);
expect(content).toMatch(/# Nested node_modules/); expect(content).toMatch(/# Nested node_modules/);
}); });
describe('babel config', () => {
it('should create babel config if not present', async () => {
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
json.namedInputs = {
sharedGlobals: ['{workspaceRoot}/exiting-file.json'],
};
return json;
});
await reactNativeInitGenerator(tree, {
unitTestRunner: 'none',
e2eTestRunner: 'none',
});
expect(tree.exists('babel.config.json')).toBe(true);
const sharedGloabls = readJson<NxJsonConfiguration>(tree, 'nx.json')
.namedInputs.sharedGlobals;
expect(sharedGloabls).toContain('{workspaceRoot}/exiting-file.json');
expect(sharedGloabls).toContain('{workspaceRoot}/babel.config.json');
});
it('should not overwrite existing babel config', async () => {
tree.write('babel.config.json', '{ "preset": ["preset-awesome"] }');
await reactNativeInitGenerator(tree, {
unitTestRunner: 'none',
e2eTestRunner: 'none',
});
const existing = readJson(tree, 'babel.config.json');
expect(existing).toEqual({ preset: ['preset-awesome'] });
});
it('should not overwrite existing babel config (.js)', async () => {
tree.write('/babel.config.js', 'module.exports = () => {};');
await reactNativeInitGenerator(tree, {
unitTestRunner: 'none',
e2eTestRunner: 'none',
});
expect(tree.exists('babel.config.json')).toBe(false);
});
});
}); });

View File

@ -10,7 +10,6 @@ import {
} from '@nx/devkit'; } from '@nx/devkit';
import { Schema } from './schema'; import { Schema } from './schema';
import { addBabelInputs } from '@nx/js/src/utils/add-babel-inputs';
import { jestInitGenerator } from '@nx/jest'; import { jestInitGenerator } from '@nx/jest';
import { detoxInitGenerator } from '@nx/detox'; import { detoxInitGenerator } from '@nx/detox';
import { babelPresetReactVersion } from '@nx/react/src/utils/versions'; import { babelPresetReactVersion } from '@nx/react/src/utils/versions';
@ -41,7 +40,6 @@ import { addGitIgnoreEntry } from './lib/add-git-ignore-entry';
export async function reactNativeInitGenerator(host: Tree, schema: Schema) { export async function reactNativeInitGenerator(host: Tree, schema: Schema) {
addGitIgnoreEntry(host); addGitIgnoreEntry(host);
await addBabelInputs(host);
const tasks: GeneratorCallback[] = []; const tasks: GeneratorCallback[] = [];
tasks.push( tasks.push(

View File

@ -1,4 +1,10 @@
import { ExecutorContext, logger, workspaceRoot } from '@nx/devkit'; import {
createProjectGraphAsync,
ExecutorContext,
logger,
ProjectGraphProjectNode,
workspaceRoot,
} from '@nx/devkit';
import { composePluginsSync } from '@nx/webpack/src/utils/config'; import { composePluginsSync } from '@nx/webpack/src/utils/config';
import { NormalizedWebpackExecutorOptions } from '@nx/webpack/src/executors/webpack/schema'; import { NormalizedWebpackExecutorOptions } from '@nx/webpack/src/executors/webpack/schema';
import { join } from 'path'; import { join } from 'path';
@ -10,6 +16,7 @@ import {
} from 'webpack'; } from 'webpack';
import { mergePlugins } from './merge-plugins'; import { mergePlugins } from './merge-plugins';
import { withReact } from '../with-react'; import { withReact } from '../with-react';
import { existsSync } from 'fs';
// This is shamelessly taken from CRA and modified for NX use // This is shamelessly taken from CRA and modified for NX use
// https://github.com/facebook/create-react-app/blob/4784997f0682e75eb32a897b4ffe34d735912e6c/packages/react-scripts/config/env.js#L71 // https://github.com/facebook/create-react-app/blob/4784997f0682e75eb32a897b4ffe34d735912e6c/packages/react-scripts/config/env.js#L71
@ -58,6 +65,90 @@ export const core = (prev, options) => ({
disableWebpackDefaults: true, disableWebpackDefaults: true,
}); });
interface NxProjectData {
workspaceRoot: string;
projectRoot: string;
sourceRoot: string;
projectNode?: ProjectGraphProjectNode;
}
const getProjectData = async (
storybookOptions: any
): Promise<NxProjectData> => {
const fallbackData = {
workspaceRoot: storybookOptions.configDir,
projectRoot: '',
sourceRoot: '',
};
// Edge-case: not running from Nx
if (!process.env.NX_WORKSPACE_ROOT) return fallbackData;
const projectGraph = await createProjectGraphAsync();
const projectNode = projectGraph.nodes[process.env.NX_TASK_TARGET_PROJECT];
return projectNode
? {
workspaceRoot: process.env.NX_WORKSPACE_ROOT,
projectRoot: projectNode.data.root,
sourceRoot: projectNode.data.sourceRoot,
projectNode,
}
: // Edge-case: missing project node
fallbackData;
};
const fixBabelConfigurationIfNeeded = (
webpackConfig: Configuration,
projectData: NxProjectData
): void => {
if (!projectData.projectNode) return;
const isUsingBabelUpwardRootMode = Object.keys(
projectData.projectNode.data.targets
).find((k) => {
const targetConfig = projectData.projectNode.data.targets[k];
return (
(targetConfig.executor === '@nx/webpack:webpack' ||
targetConfig.executor === '@nrwl/webpack:webpack') &&
targetConfig.options?.babelUpwardRootMode
);
});
if (isUsingBabelUpwardRootMode) return;
let babelrcPath: string;
for (const ext of ['', '.json', '.js', '.cjs', '.mjs', '.cts']) {
const candidate = join(
projectData.workspaceRoot,
projectData.projectRoot,
`.babelrc${ext}`
);
if (existsSync(candidate)) {
babelrcPath = candidate;
break;
}
}
// Unexpected setup, skip.
if (!babelrcPath) return;
let babelRuleItem;
for (const rule of webpackConfig.module.rules) {
if (typeof rule === 'string') continue;
if (!Array.isArray(rule.use)) continue;
for (const item of rule.use) {
if (typeof item !== 'string' && item['loader'].includes('babel-loader')) {
babelRuleItem = item;
break;
}
}
}
if (babelRuleItem) {
babelRuleItem.options.configFile = babelrcPath;
}
};
export const webpack = async ( export const webpack = async (
storybookWebpackConfig: Configuration = {}, storybookWebpackConfig: Configuration = {},
options: any options: any
@ -78,12 +169,15 @@ export const webpack = async (
const { withNx, withWeb } = require('@nx/webpack'); const { withNx, withWeb } = require('@nx/webpack');
const tsconfigPath = join(options.configDir, 'tsconfig.json'); const tsconfigPath = join(options.configDir, 'tsconfig.json');
const projectData = await getProjectData(options);
fixBabelConfigurationIfNeeded(storybookWebpackConfig, projectData);
const builderOptions: NormalizedWebpackExecutorOptions = { const builderOptions: NormalizedWebpackExecutorOptions = {
...options, ...options,
root: options.configDir, root: projectData.workspaceRoot,
// These are blank because root is the absolute path to .storybook folder projectRoot: projectData.projectRoot,
projectRoot: '', sourceRoot: projectData.sourceRoot,
sourceRoot: '',
fileReplacements: [], fileReplacements: [],
sourceMap: true, sourceMap: true,
styles: options.styles ?? [], styles: options.styles ?? [],

View File

@ -849,12 +849,12 @@ describe('app', () => {
).toBeUndefined(); ).toBeUndefined();
}); });
describe('--skipWorkspaceJson', () => { describe('--skipNxJson', () => {
it('should update workspace with defaults when --skipprojectsConfigurations=false', async () => { it('should update workspace with defaults when --skipprojectsConfigurations=false', async () => {
await applicationGenerator(appTree, { await applicationGenerator(appTree, {
...schema, ...schema,
style: 'styled-components', style: 'styled-components',
skipWorkspaceJson: false, skipNxJson: false,
}); });
const nxJson = readNxJson(appTree); const nxJson = readNxJson(appTree);

View File

@ -39,6 +39,7 @@ import { installCommonDependencies } from './lib/install-common-dependencies';
import { extractTsConfigBase } from '../../utils/create-ts-config'; import { extractTsConfigBase } from '../../utils/create-ts-config';
import { addSwcDependencies } from '@nx/js/src/utils/swc/add-swc-dependencies'; import { addSwcDependencies } from '@nx/js/src/utils/swc/add-swc-dependencies';
import * as chalk from 'chalk'; import * as chalk from 'chalk';
import { showPossibleWarnings } from './lib/show-possible-warnings';
async function addLinting(host: Tree, options: NormalizedSchema) { async function addLinting(host: Tree, options: NormalizedSchema) {
const tasks: GeneratorCallback[] = []; const tasks: GeneratorCallback[] = [];
@ -73,12 +74,7 @@ async function addLinting(host: Tree, options: NormalizedSchema) {
const installTask = await addDependenciesToPackageJson( const installTask = await addDependenciesToPackageJson(
host, host,
extraEslintDependencies.dependencies, extraEslintDependencies.dependencies,
{ extraEslintDependencies.devDependencies
...extraEslintDependencies.devDependencies,
...(options.compiler === 'swc'
? { 'swc-loader': swcLoaderVersion }
: {}),
}
); );
const addSwcTask = addSwcDependencies(host); const addSwcTask = addSwcDependencies(host);
tasks.push(installTask, addSwcTask); tasks.push(installTask, addSwcTask);
@ -94,11 +90,11 @@ export async function applicationGenerator(
const tasks = []; const tasks = [];
const options = normalizeOptions(host, schema); const options = normalizeOptions(host, schema);
showPossibleWarnings(host, options);
const initTask = await reactInitGenerator(host, { const initTask = await reactInitGenerator(host, {
...options, ...options,
skipFormat: true, skipFormat: true,
skipBabelConfig: options.bundler === 'vite',
skipHelperLibs: options.bundler === 'vite', skipHelperLibs: options.bundler === 'vite',
}); });

View File

@ -1,8 +1,10 @@
import { addDependenciesToPackageJson, Tree } from '@nx/devkit'; import { addDependenciesToPackageJson, Tree } from '@nx/devkit';
import { import {
babelPresetReactVersion,
lessVersion, lessVersion,
sassVersion, sassVersion,
stylusVersion, stylusVersion,
swcLoaderVersion,
} from '../../../utils/versions'; } from '../../../utils/versions';
import { NormalizedSchema } from '../schema'; import { NormalizedSchema } from '../schema';
@ -10,26 +12,33 @@ export function installCommonDependencies(
host: Tree, host: Tree,
options: NormalizedSchema options: NormalizedSchema
) { ) {
let devDependencies = null; const devDependencies: Record<string, string> = {};
// Vite requires style preprocessors to be installed manually. // Vite requires style preprocessors to be installed manually.
// `@nx/webpack` installs them automatically for now. // `@nx/webpack` installs them automatically for now.
// TODO(jack): Once we clean up webpack we can remove this check
if (options.bundler === 'vite' || options.unitTestRunner === 'vitest') { if (options.bundler === 'vite' || options.unitTestRunner === 'vitest') {
switch (options.style) { switch (options.style) {
case 'scss': case 'scss':
devDependencies = { sass: sassVersion }; devDependencies['sass'] = sassVersion;
break; break;
case 'less': case 'less':
devDependencies = { less: lessVersion }; devDependencies['less'] = lessVersion;
break; break;
case 'styl': // @TODO(17): deprecated, going to be removed in Nx 17 case 'styl': // @TODO(17): deprecated, going to be removed in Nx 17
devDependencies = { stylus: stylusVersion }; devDependencies['stylus'] = stylusVersion;
break; break;
} }
} }
return devDependencies if (options.bundler === 'webpack') {
? addDependenciesToPackageJson(host, {}, devDependencies) if (options.compiler === 'swc') {
: function noop() {}; devDependencies['swc-loader'] = swcLoaderVersion;
} else if (options.compiler === 'babel') {
// babel-loader is currently included in @nx/webpack
// TODO(jack): Install babel-loader and other babel packages only as needed
devDependencies['@babel/preset-react'] = babelPresetReactVersion;
}
}
return addDependenciesToPackageJson(host, {}, devDependencies);
} }

View File

@ -2,7 +2,7 @@ import { readNxJson, Tree, updateNxJson } from '@nx/devkit';
import { NormalizedSchema } from '../schema'; import { NormalizedSchema } from '../schema';
export function setDefaults(host: Tree, options: NormalizedSchema) { export function setDefaults(host: Tree, options: NormalizedSchema) {
if (options.skipWorkspaceJson) { if (options.skipNxJson) {
return; return;
} }

View File

@ -0,0 +1,16 @@
import * as chalk from 'chalk';
import { logger, Tree } from '@nx/devkit';
import { NormalizedSchema, Schema } from '../schema';
export function showPossibleWarnings(
tree: Tree,
options: NormalizedSchema<Schema>
) {
if (options.style === 'styled-jsx' && options.compiler === 'swc') {
logger.warn(
`styled-jsx may not work with SWC. Try using ${chalk.bold(
'nx g @nx/react:app --compiler=babel'
)} instead.`
);
}
}

View File

@ -14,7 +14,7 @@ export interface Schema {
pascalCaseFiles?: boolean; pascalCaseFiles?: boolean;
classComponent?: boolean; classComponent?: boolean;
routing?: boolean; routing?: boolean;
skipWorkspaceJson?: boolean; skipNxJson?: boolean;
js?: boolean; js?: boolean;
globalCss?: boolean; globalCss?: boolean;
strict?: boolean; strict?: boolean;

View File

@ -98,8 +98,8 @@
"default": false, "default": false,
"x-priority": "internal" "x-priority": "internal"
}, },
"skipWorkspaceJson": { "skipNxJson": {
"description": "Skip updating `workspace.json` with default options based on values provided to this app (e.g. babel, style).", "description": "Skip updating `nx.json` with default options based on values provided to this app.",
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"x-priority": "internal" "x-priority": "internal"

View File

@ -15,7 +15,7 @@ export interface Schema {
remotes?: string[]; remotes?: string[];
setParserOptionsProject?: boolean; setParserOptionsProject?: boolean;
skipFormat?: boolean; skipFormat?: boolean;
skipWorkspaceJson?: boolean; skipNxJson?: boolean;
ssr?: boolean; ssr?: boolean;
strict?: boolean; strict?: boolean;
style: SupportedStyles; style: SupportedStyles;

View File

@ -79,7 +79,7 @@
"default": false, "default": false,
"x-priority": "internal" "x-priority": "internal"
}, },
"skipWorkspaceJson": { "skipNxJson": {
"description": "Skip updating workspace.json with default options based on values provided to this app (e.g. babel, style).", "description": "Skip updating workspace.json with default options based on values provided to this app (e.g. babel, style).",
"type": "boolean", "type": "boolean",
"default": false, "default": false,

View File

@ -30,9 +30,4 @@ describe('init', () => {
await reactInitGenerator(tree, { ...schema, unitTestRunner: 'none' }); await reactInitGenerator(tree, { ...schema, unitTestRunner: 'none' });
expect(tree.exists('jest.config.js')).toEqual(false); expect(tree.exists('jest.config.js')).toEqual(false);
}); });
it('should not add babel.config.json if skipBabelConfig is true', async () => {
await reactInitGenerator(tree, { ...schema, skipBabelConfig: true });
expect(tree.exists('babel.config.json')).toEqual(false);
});
}); });

View File

@ -8,7 +8,6 @@ import {
runTasksInSerial, runTasksInSerial,
Tree, Tree,
updateNxJson, updateNxJson,
writeJson,
} from '@nx/devkit'; } from '@nx/devkit';
import { initGenerator as jsInitGenerator } from '@nx/js'; import { initGenerator as jsInitGenerator } from '@nx/js';
@ -65,25 +64,6 @@ function updateDependencies(host: Tree, schema: InitSchema) {
}); });
} }
function initRootBabelConfig(tree: Tree, schema: InitSchema) {
if (tree.exists('/babel.config.json') || tree.exists('/babel.config.js')) {
return;
}
if (!schema.skipBabelConfig) {
writeJson(tree, '/babel.config.json', {
babelrcRoots: ['*'], // Make sure .babelrc files other than root can be loaded in a monorepo
});
}
const nxJson = readNxJson(tree);
if (nxJson.namedInputs?.sharedGlobals) {
nxJson.namedInputs.sharedGlobals.push('{workspaceRoot}/babel.config.json');
}
updateNxJson(tree, nxJson);
}
export async function reactInitGenerator(host: Tree, schema: InitSchema) { export async function reactInitGenerator(host: Tree, schema: InitSchema) {
const tasks: GeneratorCallback[] = []; const tasks: GeneratorCallback[] = [];
@ -106,21 +86,6 @@ export async function reactInitGenerator(host: Tree, schema: InitSchema) {
tasks.push(cypressTask); tasks.push(cypressTask);
} }
if (!schema.skipPackageJson && !schema.skipBabelConfig) {
const installBabelTask = addDependenciesToPackageJson(
host,
{},
{
'@babel/preset-react': babelPresetReactVersion,
}
);
tasks.push(installBabelTask);
}
if (!schema.skipBabelConfig) {
initRootBabelConfig(host, schema);
}
if (!schema.skipPackageJson) { if (!schema.skipPackageJson) {
const installTask = updateDependencies(host, schema); const installTask = updateDependencies(host, schema);
tasks.push(installTask); tasks.push(installTask);

View File

@ -1,7 +1,6 @@
export interface InitSchema { export interface InitSchema {
unitTestRunner?: 'jest' | 'vitest' | 'none'; unitTestRunner?: 'jest' | 'vitest' | 'none';
e2eTestRunner?: 'cypress' | 'none'; e2eTestRunner?: 'cypress' | 'none';
skipBabelConfig?: boolean;
skipFormat?: boolean; skipFormat?: boolean;
skipPackageJson?: boolean; skipPackageJson?: boolean;
skipHelperLibs?: boolean; skipHelperLibs?: boolean;

View File

@ -28,11 +28,6 @@
"type": "boolean", "type": "boolean",
"default": false "default": false
}, },
"skipBabelConfig": {
"description": "Do not generate a root babel.config.json (if babel is not needed).",
"type": "boolean",
"default": false
},
"skipHelperLibs": { "skipHelperLibs": {
"description": "Do not install tslib.", "description": "Do not install tslib.",
"type": "boolean", "type": "boolean",

View File

@ -1,16 +0,0 @@
{
"presets": [
[
"@nx/react/babel", {
"runtime": "automatic",
"useBuiltIns": "usage"
<% if (style === '@emotion/styled') { %>,"importSource": "@emotion/react"<% } %>
}
]
],
"plugins": [
<% if (style === 'styled-components') { %>["styled-components", { "pure": true, "ssr": true }]<% } %>
<% if (style === 'styled-jsx') { %>"styled-jsx/babel"<% } %>
<% if (style === '@emotion/styled') { %>"@emotion/babel-plugin"<% } %>
]
}

View File

@ -5,6 +5,7 @@ import {
names, names,
offsetFromRoot, offsetFromRoot,
toJS, toJS,
writeJson,
} from '@nx/devkit'; } from '@nx/devkit';
import { getRelativePathToRootTsConfig } from '@nx/js'; import { getRelativePathToRootTsConfig } from '@nx/js';
@ -38,10 +39,33 @@ export function createFiles(host: Tree, options: NormalizedSchema) {
options.projectRoot, options.projectRoot,
substitutions substitutions
); );
if (host.exists(joinPathFragments(options.projectRoot, '.babelrc'))) {
host.delete(joinPathFragments(options.projectRoot, '.babelrc'));
} }
if (options.compiler === 'babel') {
writeJson(host, joinPathFragments(options.projectRoot, '.babelrc'), {
presets: [
[
'@nx/react/babel',
{
runtime: 'automatic',
useBuiltIns: 'usage',
importSource:
options.style === '@emotion/styled'
? '@emotion/react'
: undefined,
},
],
],
plugins: [
options.style === 'styled-components'
? ['styled-components', { pure: true, ssr: true }]
: undefined,
options.style === 'styled-jsx' ? 'styled-jsx/babel' : undefined,
options.style === '@emotion/styled'
? '@emotion/babel-plugin'
: undefined,
].filter(Boolean),
});
} }
if (!options.publishable && !options.buildable) { if (!options.publishable && !options.buildable) {

View File

@ -6,6 +6,7 @@ import {
} from '@nx/devkit'; } from '@nx/devkit';
import { addSwcDependencies } from '@nx/js/src/utils/swc/add-swc-dependencies'; import { addSwcDependencies } from '@nx/js/src/utils/swc/add-swc-dependencies';
import { import {
babelPresetReactVersion,
lessVersion, lessVersion,
reactDomVersion, reactDomVersion,
reactVersion, reactVersion,
@ -50,6 +51,14 @@ export function installCommonDependencies(
if (options.compiler === 'swc') { if (options.compiler === 'swc') {
tasks.push(addSwcDependencies(host)); tasks.push(addSwcDependencies(host));
} else if (options.compiler === 'babel') {
tasks.push(
addDependenciesToPackageJson(
host,
{},
{ '@babel/preset-react': babelPresetReactVersion }
)
);
} }
return runTasksInSerial(...tasks); return runTasksInSerial(...tasks);

View File

@ -47,7 +47,6 @@ describe('lib', () => {
}); });
}); });
describe('not nested', () => {
it('should update project configuration', async () => { it('should update project configuration', async () => {
await libraryGenerator(tree, defaultSchema); await libraryGenerator(tree, defaultSchema);
const project = readProjectConfiguration(tree, 'my-lib'); const project = readProjectConfiguration(tree, 'my-lib');
@ -69,10 +68,7 @@ describe('lib', () => {
unitTestRunner: 'vitest', unitTestRunner: 'vitest',
}); });
const tsconfigApp = readJson(tree, 'libs/my-lib/tsconfig.lib.json'); const tsconfigApp = readJson(tree, 'libs/my-lib/tsconfig.lib.json');
expect(tsconfigApp.compilerOptions.types).toEqual([ expect(tsconfigApp.compilerOptions.types).toEqual(['node', 'vite/client']);
'node',
'vite/client',
]);
const tsconfigSpec = readJson(tree, 'libs/my-lib/tsconfig.spec.json'); const tsconfigSpec = readJson(tree, 'libs/my-lib/tsconfig.spec.json');
expect(tsconfigSpec.compilerOptions.types).toEqual([ expect(tsconfigSpec.compilerOptions.types).toEqual([
'vitest/globals', 'vitest/globals',
@ -239,6 +235,17 @@ describe('lib', () => {
"['babel-jest', { presets: ['@nx/react/babel'] }]" "['babel-jest', { presets: ['@nx/react/babel'] }]"
); );
}); });
it('should add @babel/preset-react when using babel compiler', async () => {
await libraryGenerator(tree, {
...defaultSchema,
compiler: 'babel',
directory: 'myDir',
tags: 'one',
});
const packageJson = readJson(tree, 'package.json');
expect(packageJson.devDependencies['@babel/preset-react']).toBeDefined();
}); });
describe('nested', () => { describe('nested', () => {

View File

@ -43,7 +43,6 @@ export async function libraryGenerator(host: Tree, schema: Schema) {
...options, ...options,
e2eTestRunner: 'none', e2eTestRunner: 'none',
skipFormat: true, skipFormat: true,
skipBabelConfig: options.bundler === 'vite' || options.compiler === 'swc',
skipHelperLibs: options.bundler === 'vite', skipHelperLibs: options.bundler === 'vite',
}); });
tasks.push(initTask); tasks.push(initTask);

View File

@ -17,7 +17,7 @@ export interface Schema {
routing?: boolean; routing?: boolean;
setParserOptionsProject?: boolean; setParserOptionsProject?: boolean;
skipFormat: boolean; skipFormat: boolean;
skipWorkspaceJson?: boolean; skipNxJson?: boolean;
ssr?: boolean; ssr?: boolean;
strict?: boolean; strict?: boolean;
style: SupportedStyles; style: SupportedStyles;

View File

@ -84,7 +84,7 @@
"default": false, "default": false,
"x-priority": "internal" "x-priority": "internal"
}, },
"skipWorkspaceJson": { "skipNxJson": {
"description": "Skip updating workspace.json with default options based on values provided to this app (e.g. babel, style).", "description": "Skip updating workspace.json with default options based on values provided to this app (e.g. babel, style).",
"type": "boolean", "type": "boolean",
"default": false, "default": false,

View File

@ -11,23 +11,6 @@ describe('rollupInitGenerator', () => {
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
}); });
it('should support babel', async () => {
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
json.namedInputs = {
sharedGlobals: ['{workspaceRoot}/exiting-file.json'],
};
return json;
});
await rollupInitGenerator(tree, { compiler: 'babel' });
expect(tree.exists('babel.config.json'));
const sharedGlobals = readJson<NxJsonConfiguration>(tree, 'nx.json')
.namedInputs.sharedGlobals;
expect(sharedGlobals).toContain('{workspaceRoot}/exiting-file.json');
expect(sharedGlobals).toContain('{workspaceRoot}/babel.config.json');
});
it('should support swc', async () => { it('should support swc', async () => {
await rollupInitGenerator(tree, { compiler: 'swc' }); await rollupInitGenerator(tree, { compiler: 'swc' });

View File

@ -17,10 +17,6 @@ import { addBabelInputs } from '@nx/js/src/utils/add-babel-inputs';
export async function rollupInitGenerator(tree: Tree, schema: Schema) { export async function rollupInitGenerator(tree: Tree, schema: Schema) {
let task: GeneratorCallback; let task: GeneratorCallback;
if (schema.compiler === 'babel') {
addBabelInputs(tree);
}
if (schema.compiler === 'swc') { if (schema.compiler === 'swc') {
task = addDependenciesToPackageJson( task = addDependenciesToPackageJson(
tree, tree,

View File

@ -184,8 +184,6 @@ export async function applicationGenerator(host: Tree, schema: Schema) {
const webTask = await webInitGenerator(host, { const webTask = await webInitGenerator(host, {
...options, ...options,
skipFormat: true, skipFormat: true,
// Vite does not use babel by default
skipBabelConfig: options.bundler === 'vite',
}); });
tasks.push(webTask); tasks.push(webTask);

View File

@ -46,8 +46,8 @@
"compiler": { "compiler": {
"type": "string", "type": "string",
"description": "The compiler to use", "description": "The compiler to use",
"enum": ["babel", "swc"], "enum": ["swc", "babel"],
"default": "babel", "default": "swc",
"x-priority": "important" "x-priority": "important"
}, },
"bundler": { "bundler": {

View File

@ -8,8 +8,6 @@ import {
runTasksInSerial, runTasksInSerial,
Tree, Tree,
} from '@nx/devkit'; } from '@nx/devkit';
import { addBabelInputs } from '@nx/js/src/utils/add-babel-inputs';
import { initGenerator as jsInitGenerator } from '@nx/js'; import { initGenerator as jsInitGenerator } from '@nx/js';
import { import {
nxVersion, nxVersion,
@ -65,7 +63,6 @@ export async function webInitGenerator(tree: Tree, schema: Schema) {
const installTask = updateDependencies(tree, schema); const installTask = updateDependencies(tree, schema);
tasks.push(installTask); tasks.push(installTask);
} }
addBabelInputs(tree);
if (!schema.skipFormat) { if (!schema.skipFormat) {
await formatFiles(tree); await formatFiles(tree);

View File

@ -4,5 +4,4 @@ export interface Schema {
e2eTestRunner?: 'cypress' | 'none'; e2eTestRunner?: 'cypress' | 'none';
skipFormat?: boolean; skipFormat?: boolean;
skipPackageJson?: boolean; skipPackageJson?: boolean;
skipBabelConfig?: boolean;
} }

View File

@ -34,11 +34,6 @@
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"x-priority": "internal" "x-priority": "internal"
},
"skipBabelConfig": {
"description": "Do not generate a root babel.config.json (if babel is not needed).",
"type": "boolean",
"default": false
} }
}, },
"required": [] "required": []

View File

@ -70,28 +70,16 @@ export interface WebpackExecutorOptions {
webpackConfig?: string; webpackConfig?: string;
babelConfig?: string; babelConfig?: string;
babelUpwardRootMode?: boolean; babelUpwardRootMode?: boolean;
// TODO(jack): Also deprecate these in schema.json once we have migration from executor options to webpack.config.js file.
/** @deprecated Moved to withWeb options from `@nx/webpack` */
baseHref?: string; baseHref?: string;
/** @deprecated Moved to withWeb options from `@nx/webpack` */
crossOrigin?: 'none' | 'anonymous' | 'use-credentials'; crossOrigin?: 'none' | 'anonymous' | 'use-credentials';
/** @deprecated Moved to withWeb options from `@nx/webpack` */
deployUrl?: string; deployUrl?: string;
/** @deprecated Moved to withWeb options from `@nx/webpack` */
extractCss?: boolean; extractCss?: boolean;
/** @deprecated Moved to withWeb options from `@nx/webpack` */
generateIndexHtml?: boolean; generateIndexHtml?: boolean;
/** @deprecated Moved to withWeb options from `@nx/webpack` */
index?: string; index?: string;
/** @deprecated Moved to withWeb options from `@nx/webpack` */
postcssConfig?: string; postcssConfig?: string;
/** @deprecated Moved to withWeb options from `@nx/webpack` */
scripts?: Array<ExtraEntryPointClass | string>; scripts?: Array<ExtraEntryPointClass | string>;
/** @deprecated Moved to withWeb options from `@nx/webpack` */
stylePreprocessorOptions?: any; stylePreprocessorOptions?: any;
/** @deprecated Moved to withWeb options from `@nx/webpack` */
styles?: Array<ExtraEntryPointClass | string>; styles?: Array<ExtraEntryPointClass | string>;
/** @deprecated Moved to withWeb options from `@nx/webpack` */
subresourceIntegrity?: boolean; subresourceIntegrity?: boolean;
} }

View File

@ -10,23 +10,6 @@ describe('webpackInitGenerator', () => {
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' }); tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
}); });
it('should support babel', async () => {
updateJson<NxJsonConfiguration>(tree, 'nx.json', (json) => {
json.namedInputs = {
sharedGlobals: ['{workspaceRoot}/exiting-file.json'],
};
return json;
});
await webpackInitGenerator(tree, { compiler: 'babel' });
expect(tree.exists('babel.config.json'));
const sharedGlobals = readJson<NxJsonConfiguration>(tree, 'nx.json')
.namedInputs.sharedGlobals;
expect(sharedGlobals).toContain('{workspaceRoot}/exiting-file.json');
expect(sharedGlobals).toContain('{workspaceRoot}/babel.config.json');
});
it('should support swc', async () => { it('should support swc', async () => {
await webpackInitGenerator(tree, { compiler: 'swc' }); await webpackInitGenerator(tree, { compiler: 'swc' });

View File

@ -22,9 +22,6 @@ import { addBabelInputs } from '@nx/js/src/utils/add-babel-inputs';
export async function webpackInitGenerator(tree: Tree, schema: Schema) { export async function webpackInitGenerator(tree: Tree, schema: Schema) {
const tasks: GeneratorCallback[] = []; const tasks: GeneratorCallback[] = [];
if (schema.compiler === 'babel') {
addBabelInputs(tree);
}
const devDependencies = { const devDependencies = {
'@nx/webpack': nxVersion, '@nx/webpack': nxVersion,
}; };

View File

@ -21,7 +21,7 @@
"type": "string", "type": "string",
"enum": ["babel", "swc", "tsc"], "enum": ["babel", "swc", "tsc"],
"description": "The compiler to use to build source.", "description": "The compiler to use to build source.",
"default": "babel" "default": "swc"
}, },
"main": { "main": {
"type": "string", "type": "string",

View File

@ -1,10 +1,11 @@
import type { ProjectConfiguration, Tree } from '@nx/devkit'; import type { Tree } from '@nx/devkit';
import { import {
convertNxGenerator, convertNxGenerator,
formatFiles, formatFiles,
joinPathFragments, joinPathFragments,
readProjectConfiguration, readProjectConfiguration,
updateProjectConfiguration, updateProjectConfiguration,
writeJson,
} from '@nx/devkit'; } from '@nx/devkit';
import { webpackInitGenerator } from '../init/init'; import { webpackInitGenerator } from '../init/init';
@ -58,7 +59,7 @@ function addBuildTarget(tree: Tree, options: WebpackProjectGeneratorSchema) {
const buildOptions: WebpackExecutorOptions = { const buildOptions: WebpackExecutorOptions = {
target: options.target, target: options.target,
outputPath: joinPathFragments('dist', project.root), outputPath: joinPathFragments('dist', project.root),
compiler: options.compiler ?? 'babel', compiler: options.compiler ?? 'swc',
main: options.main ?? joinPathFragments(project.root, 'src/main.ts'), main: options.main ?? joinPathFragments(project.root, 'src/main.ts'),
tsConfig: tsConfig:
options.tsConfig ?? joinPathFragments(project.root, 'tsconfig.app.json'), options.tsConfig ?? joinPathFragments(project.root, 'tsconfig.app.json'),
@ -71,8 +72,11 @@ function addBuildTarget(tree: Tree, options: WebpackProjectGeneratorSchema) {
if (options.babelConfig) { if (options.babelConfig) {
buildOptions.babelConfig = options.babelConfig; buildOptions.babelConfig = options.babelConfig;
} else { } else if (options.compiler === 'babel') {
buildOptions.babelUpwardRootMode = true; // If no babel config file is provided then write a default one, otherwise build will fail.
writeJson(tree, joinPathFragments(project.root, '.babelrc'), {
presets: ['@nx/js/babel'],
});
} }
if (options.target === 'web') { if (options.target === 'web') {