fix(webpack): add babelUpwardRootMode (#15061)

This commit is contained in:
Katerina Skroumpelou 2023-02-17 19:09:57 +02:00 committed by GitHub
parent 2011e29dc3
commit 5d54f71398
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 156 additions and 104 deletions

View File

@ -388,6 +388,16 @@
"x-completion-type": "file",
"x-completion-glob": "webpack?(*)@(.js|.ts)",
"x-priority": "important"
},
"babelUpwardRootMode": {
"type": "boolean",
"description": "Whether to set rootmode to upward. See https://babeljs.io/docs/en/options#rootmode",
"default": false
},
"babelConfig": {
"type": "string",
"description": "Path to the babel configuration file of your project. If not provided, Nx will default to the .babelrc file at the root of your project. See https://babeljs.io/docs/en/config-files",
"x-completion-type": "file"
}
},
"required": ["tsConfig", "main"],

View File

@ -66,6 +66,11 @@
"type": "string",
"description": "Path relative to workspace root to a custom webpack file that takes a config object and returns an updated config.",
"x-priority": "internal"
},
"babelConfig": {
"type": "string",
"description": "Optionally specify a path relative to workspace root to the babel configuration file of your project.",
"x-completion-type": "file"
}
},
"required": [],

View File

@ -34,13 +34,6 @@ describe('js e2e', () => {
const libPackageJson = readJson(`libs/${lib}/package.json`);
expect(libPackageJson.scripts).toBeUndefined();
// Since `@nrwl/web` is installed in workspace, .babelrc and babel preset are needed for this lib
const babelRc = readJson(`libs/${lib}/.babelrc`);
expect(babelRc.plugins).toBeUndefined();
expect(babelRc.presets).toStrictEqual([
['@nrwl/js/babel', { useBuiltIns: 'usage' }],
]);
expect(runCLI(`build ${lib}`)).toContain('Done compiling TypeScript files');
checkFilesExist(
`dist/libs/${lib}/README.md`,

View File

@ -927,38 +927,6 @@ describe('lib', () => {
`);
});
it('should generate a .babelrc when flag is not set and there is a `@nrwl/web` package installed', async () => {
updateJson(tree, 'package.json', (json) => {
json.devDependencies = {
'@nrwl/web': '1.1.1',
'@nrwl/react': '1.1.1',
'@nrwl/next': '1.1.1',
};
return json;
});
await libraryGenerator(tree, {
...defaultOptions,
name: 'myLib',
includeBabelRc: undefined,
});
expect(tree.exists('libs/my-lib/.babelrc')).toBeTruthy();
const babelRc = readJson(tree, 'libs/my-lib/.babelrc');
expect(babelRc).toMatchInlineSnapshot(`
Object {
"presets": Array [
Array [
"@nrwl/js/babel",
Object {
"useBuiltIns": "usage",
},
],
],
}
`);
});
it('should not generate a .babelrc when flag is not set and there is NOT a `@nrwl/web` package installed', async () => {
updateJson(tree, 'package.json', (json) => {
json.devDependencies = {

View File

@ -162,6 +162,10 @@ function addProject(
},
};
if (options.bundler === 'webpack') {
projectConfiguration.targets.build.options.babelUpwardRootMode = true;
}
if (options.compiler === 'swc' && options.skipTypeCheck) {
projectConfiguration.targets.build.options.skipTypeCheck = true;
}
@ -234,30 +238,6 @@ function updateTsConfig(tree: Tree, options: NormalizedSchema) {
});
}
/**
* Currently `@nrwl/js:library` TypeScript files can be compiled by most NX applications scaffolded via the Plugin system. However, `@nrwl/react:app` is an exception that due to its babel configuration, won't transpile external TypeScript files from packages/libs that do not contain a .babelrc.
*
* If a user doesn't explicitly set the flag, to prevent breaking the experience (they see the application failing, and they need to manually add the babelrc themselves), we want to detect whether they have the `@nrwl/web` plugin installed, and generate it automatically for them (even when they do not explicity request it).
*
* You can find more details on why this is necessary here:
* https://github.com/nrwl/nx/pull/10055
*/
function shouldAddBabelRc(tree: Tree, options: NormalizedSchema) {
if (typeof options.includeBabelRc === 'undefined') {
const webPluginName = '@nrwl/web';
const packageJson = readJson(tree, 'package.json');
const hasNxWebPlugin = Object.keys(
packageJson.devDependencies as Record<string, string>
).includes(webPluginName);
return hasNxWebPlugin;
}
return options.includeBabelRc;
}
function addBabelRc(tree: Tree, options: NormalizedSchema) {
const filename = '.babelrc';
@ -289,7 +269,7 @@ function createFiles(tree: Tree, options: NormalizedSchema, filesDir: string) {
if (options.compiler === 'swc') {
addSwcDependencies(tree);
addSwcConfig(tree, options.projectRoot);
} else if (shouldAddBabelRc(tree, options)) {
} else if (options.includeBabelRc) {
addBabelRc(tree, options);
}

View File

@ -3,7 +3,6 @@ import storiesGenerator from '../stories/stories';
import {
convertNxGenerator,
ensurePackage,
joinPathFragments,
logger,
readProjectConfiguration,
Tree,
@ -54,42 +53,6 @@ export async function storybookConfigurationGenerator(
}
}
/**
* If it's library and there's no .babelrc file,
* we need to generate one if it's not using vite.
*
* The reason is that it will be using webpack for Storybook,
* and webpack needs the babelrc file to be present.
*
* The reason the babelrc file is not there in the first place,
* is because the vitest generator deletes it, since it
* does not need it.
* See:
* packages/react/src/generators/library/lib/create-files.ts#L42
*/
if (
bundler !== 'vite' &&
projectConfig.projectType === 'library' &&
!host.exists(joinPathFragments(projectConfig.root, '.babelrc'))
) {
host.write(
joinPathFragments(projectConfig.root, '.babelrc'),
JSON.stringify({
presets: [
[
'@nrwl/react/babel',
{
runtime: 'automatic',
useBuiltIns: 'usage',
},
],
],
plugins: [],
})
);
}
const installTask = await configurationGenerator(host, {
name: schema.name,
uiFramework: '@storybook/react',

View File

@ -100,6 +100,10 @@ async function setupBundler(tree: Tree, options: NormalizedSchema) {
buildOptions.styles = [
joinPathFragments(options.appProjectRoot, `src/styles.${options.style}`),
];
// We can delete that, because this projest is an application
// and applications have a .babelrc file in their root dir.
// So Nx will find it and use it
delete buildOptions.babelUpwardRootMode;
buildOptions.scripts = [];
prodConfig.fileReplacements = [
{

View File

@ -17,6 +17,12 @@
"version": "15.6.3-beta.0",
"description": "Creates or updates webpack.config.js file with the new options for webpack.",
"factory": "./src/migrations/update-15-6-3/webpack-config-setup"
},
"add-babelUpwardRootMode-flag": {
"cli": "nx",
"version": "15.7.2-beta.0",
"description": "Add the babelUpwardRootMode option to the build executor options.",
"factory": "./src/migrations/update-15-7-2/add-babelUpwardRootMode-flag"
}
},
"packageJsonUpdates": {}

View File

@ -68,6 +68,8 @@ export interface WebpackExecutorOptions {
verbose?: boolean;
watch?: boolean;
webpackConfig?: string;
babelConfig?: string;
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 `@nrwl/webpack` */
baseHref?: string;

View File

@ -309,6 +309,16 @@
"x-completion-type": "file",
"x-completion-glob": "webpack?(*)@(.js|.ts)",
"x-priority": "important"
},
"babelUpwardRootMode": {
"type": "boolean",
"description": "Whether to set rootmode to upward. See https://babeljs.io/docs/en/options#rootmode",
"default": false
},
"babelConfig": {
"type": "string",
"description": "Path to the babel configuration file of your project. If not provided, Nx will default to the .babelrc file at the root of your project. See https://babeljs.io/docs/en/config-files",
"x-completion-type": "file"
}
},
"required": ["tsConfig", "main"],

View File

@ -9,4 +9,5 @@ export interface WebpackProjectGeneratorSchema {
skipValidation?: boolean;
target?: 'node' | 'web';
webpackConfig?: string;
babelConfig?: string;
}

View File

@ -66,6 +66,11 @@
"type": "string",
"description": "Path relative to workspace root to a custom webpack file that takes a config object and returns an updated config.",
"x-priority": "internal"
},
"babelConfig": {
"type": "string",
"description": "Optionally specify a path relative to workspace root to the babel configuration file of your project.",
"x-completion-type": "file"
}
},
"required": []

View File

@ -61,6 +61,12 @@ function addBuildTarget(tree: Tree, options: WebpackProjectGeneratorSchema) {
buildOptions.webpackConfig = options.webpackConfig;
}
if (options.babelConfig) {
buildOptions.babelConfig = options.babelConfig;
} else {
buildOptions.babelUpwardRootMode = true;
}
updateProjectConfiguration(tree, options.project, {
...project,
targets: {

View File

@ -0,0 +1,57 @@
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import {
addProjectConfiguration,
readProjectConfiguration,
Tree,
} from '@nrwl/devkit';
import addBabelUpwardRootModeFlag from './add-babelUpwardRootMode-flag';
describe('15.7.2 migration (add babelUpwardRootMode flag)', () => {
let tree: Tree;
beforeEach(async () => {
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
});
it('should add the babelUpwardRootMode flag to webpack projects', async () => {
addProjectConfiguration(tree, 'app1', {
root: 'apps/app1',
targets: {
build: {
executor: '@nrwl/webpack:webpack',
options: {},
},
},
});
addProjectConfiguration(tree, 'app2', {
root: 'apps/app2',
targets: {
build: {
executor: '@nrwl/webpack:webpack',
options: {
babelUpwardRootMode: false,
},
},
},
});
addProjectConfiguration(tree, 'app3', {
root: 'apps/app3',
targets: {
build: {
executor: '@nrwl/vite:build',
options: {},
},
},
});
await addBabelUpwardRootModeFlag(tree);
const app1 = readProjectConfiguration(tree, 'app1');
const app2 = readProjectConfiguration(tree, 'app2');
const app3 = readProjectConfiguration(tree, 'app3');
expect(app1.targets['build'].options.babelUpwardRootMode).toBeTruthy();
expect(app2.targets['build'].options.babelUpwardRootMode).toBeFalsy();
expect(app3.targets['build'].options.babelUpwardRootMode).toBeUndefined();
});
});

View File

@ -0,0 +1,32 @@
import {
formatFiles,
readProjectConfiguration,
Tree,
updateProjectConfiguration,
} from '@nrwl/devkit';
import { forEachExecutorOptions } from '@nrwl/workspace/src/utilities/executor-options-utils';
import { WebpackExecutorOptions } from '../../executors/webpack/schema';
export default async function (tree: Tree) {
forEachExecutorOptions<WebpackExecutorOptions>(
tree,
'@nrwl/webpack:webpack',
(
options: WebpackExecutorOptions,
projectName,
targetName,
_configurationName
) => {
if (options.babelUpwardRootMode !== undefined) {
return;
}
const projectConfiguration = readProjectConfiguration(tree, projectName);
projectConfiguration.targets[targetName].options.babelUpwardRootMode =
true;
updateProjectConfiguration(tree, projectName, projectConfiguration);
}
);
await formatFiles(tree);
}

View File

@ -350,21 +350,31 @@ export function createLoaderFromCompiler(
};
case 'babel':
const tsConfig = readTsConfig(options.tsConfig);
return {
const babelConfig = {
test: /\.([jt])sx?$/,
loader: path.join(__dirname, './web-babel-loader'),
exclude: /node_modules/,
options: {
rootMode: 'upward',
cwd: path.join(options.root, options.sourceRoot),
emitDecoratorMetadata: tsConfig.options.emitDecoratorMetadata,
isModern: true,
envName: process.env.NODE_ENV,
babelrc: true,
cacheDirectory: true,
cacheCompression: false,
},
};
if (options.babelUpwardRootMode) {
babelConfig.options['rootMode'] = 'upward';
babelConfig.options['babelrc'] = true;
} else {
babelConfig.options['configFile'] =
babelConfig.options?.['babelConfig'] ??
path.join(options.root, options.projectRoot, '.babelrc');
}
return babelConfig;
default:
return null;
}