diff --git a/e2e/js/src/js-tsc.test.ts b/e2e/js/src/js-tsc.test.ts index b3e49cfebd478..d35194024d8bb 100644 --- a/e2e/js/src/js-tsc.test.ts +++ b/e2e/js/src/js-tsc.test.ts @@ -38,7 +38,7 @@ describe('js e2e', () => { const babelRc = readJson(`libs/${lib}/.babelrc`); expect(babelRc.plugins).toBeUndefined(); expect(babelRc.presets).toStrictEqual([ - ['@nrwl/web/babel', { useBuiltIns: 'usage' }], + ['@nrwl/js/babel', { useBuiltIns: 'usage' }], ]); expect(runCLI(`build ${lib}`)).toContain('Done compiling TypeScript files'); diff --git a/packages/jest/src/generators/init/init.ts b/packages/jest/src/generators/init/init.ts index d2477de343a96..f5480ca9ba2bc 100644 --- a/packages/jest/src/generators/init/init.ts +++ b/packages/jest/src/generators/init/init.ts @@ -160,8 +160,8 @@ function updateDependencies(tree: Tree, options: NormalizedSchema) { if (options.compiler === 'babel' || options.babelJest) { devDeps['babel-jest'] = babelJestVersion; - // in some cases @nrwl/web will not already be present i.e. node only projects - devDeps['@nrwl/web'] = nxVersion; + // in some cases @nrwl/js will not already be present i.e. node only projects + devDeps['@nrwl/js'] = nxVersion; } else if (options.compiler === 'swc') { devDeps['@swc/jest'] = swcJestVersion; } diff --git a/packages/js/babel.ts b/packages/js/babel.ts new file mode 100644 index 0000000000000..6ff992d615d0d --- /dev/null +++ b/packages/js/babel.ts @@ -0,0 +1,110 @@ +import { dirname } from 'path'; + +/* + * Babel preset to provide TypeScript support and module/nomodule for Nx. + */ + +export interface NxWebBabelPresetOptions { + useBuiltIns?: boolean | string; + decorators?: { + decoratorsBeforeExport?: boolean; + legacy?: boolean; + }; + classProperties?: { + loose?: boolean; + }; +} + +module.exports = function (api: any, options: NxWebBabelPresetOptions = {}) { + api.assertVersion(7); + + const isModern = api.caller((caller) => caller?.isModern); + + // This is set by `@nrwl/web:rollup` executor + const isNxPackage = api.caller((caller) => caller?.isNxPackage); + + const emitDecoratorMetadata = api.caller( + (caller) => caller?.emitDecoratorMetadata ?? true + ); + + // Determine settings for `@babel/plugin-proposal-class-properties`, + // so that we can sync the `loose` option with `@babel/preset-env`. + const classProperties = options.classProperties ?? { loose: true }; + + return { + presets: [ + // Support module/nomodule pattern. + [ + require.resolve('@babel/preset-env'), + // For Jest tests, NODE_ENV is set as 'test' and we only want to set target as Node. + // All other options will fail in Jest since Node does not support some ES features + // such as import syntax. + process.env.NODE_ENV === 'test' + ? { targets: { node: 'current' }, loose: true } + : { + // Allow importing core-js in entrypoint and use browserlist to select polyfills. + useBuiltIns: options.useBuiltIns ?? 'entry', + corejs: 3, + // Do not transform modules to CJS + modules: false, + targets: isModern ? { esmodules: 'intersect' } : undefined, + bugfixes: true, + // Exclude transforms that make all code slower + exclude: ['transform-typeof-symbol'], + // This must match the setting for `@babel/plugin-proposal-class-properties` + loose: classProperties.loose, + }, + ], + [ + require.resolve('@babel/preset-typescript'), + { + allowDeclareFields: true, + }, + ], + ], + plugins: [ + !isNxPackage + ? [ + require.resolve('@babel/plugin-transform-runtime'), + { + corejs: false, + helpers: true, + regenerator: true, + useESModules: isModern, + absoluteRuntime: dirname( + require.resolve('@babel/runtime/package.json') + ), + }, + ] + : null, + require.resolve('babel-plugin-macros'), + emitDecoratorMetadata + ? require.resolve('babel-plugin-transform-typescript-metadata') + : undefined, + // Must use legacy decorators to remain compatible with TypeScript. + [ + require.resolve('@babel/plugin-proposal-decorators'), + options.decorators ?? { legacy: true }, + ], + [ + require.resolve('@babel/plugin-proposal-class-properties'), + classProperties, + ], + ].filter(Boolean), + overrides: [ + // Convert `const enum` to `enum`. The former cannot be supported by babel + // but at least we can get it to not error out. + { + test: /\.tsx?$/, + plugins: [ + [ + require.resolve('babel-plugin-const-enum'), + { + transform: 'removeConst', + }, + ], + ], + }, + ], + }; +}; diff --git a/packages/js/package.json b/packages/js/package.json index 68cc37f2f5dae..79a2278d67da4 100644 --- a/packages/js/package.json +++ b/packages/js/package.json @@ -32,9 +32,19 @@ "executors": "./executors.json", "builders": "./executors.json", "dependencies": { + "@babel/core": "^7.15.0", + "@babel/plugin-proposal-class-properties": "^7.14.5", + "@babel/plugin-proposal-decorators": "^7.14.5", + "@babel/plugin-transform-runtime": "^7.15.0", + "@babel/preset-env": "^7.15.0", + "@babel/preset-typescript": "^7.15.0", + "@babel/runtime": "^7.14.8", "@nrwl/devkit": "file:../devkit", "@nrwl/linter": "file:../linter", "@nrwl/workspace": "file:../workspace", + "babel-plugin-const-enum": "^1.0.1", + "babel-plugin-macros": "^2.8.0", + "babel-plugin-transform-typescript-metadata": "^0.3.1", "chalk": "^4.1.0", "fast-glob": "3.2.7", "fs-extra": "^11.1.0", diff --git a/packages/js/src/generators/library/library.spec.ts b/packages/js/src/generators/library/library.spec.ts index 8bb7fd3469cc9..b20667f07bd8d 100644 --- a/packages/js/src/generators/library/library.spec.ts +++ b/packages/js/src/generators/library/library.spec.ts @@ -917,7 +917,7 @@ describe('lib', () => { Object { "presets": Array [ Array [ - "@nrwl/web/babel", + "@nrwl/js/babel", Object { "useBuiltIns": "usage", }, @@ -950,7 +950,7 @@ describe('lib', () => { Object { "presets": Array [ Array [ - "@nrwl/web/babel", + "@nrwl/js/babel", Object { "useBuiltIns": "usage", }, diff --git a/packages/js/src/generators/library/library.ts b/packages/js/src/generators/library/library.ts index 1acdd7c496576..8f767667c07da 100644 --- a/packages/js/src/generators/library/library.ts +++ b/packages/js/src/generators/library/library.ts @@ -256,7 +256,7 @@ function addBabelRc(tree: Tree, options: NormalizedSchema) { const filename = '.babelrc'; const babelrc = { - presets: [['@nrwl/web/babel', { useBuiltIns: 'usage' }]], + presets: [['@nrwl/js/babel', { useBuiltIns: 'usage' }]], }; writeJson(tree, join(options.projectRoot, filename), babelrc); diff --git a/packages/react/babel.ts b/packages/react/babel.ts index d4d946d64a76a..9573989790bf8 100644 --- a/packages/react/babel.ts +++ b/packages/react/babel.ts @@ -1,12 +1,18 @@ -import type { NxWebBabelPresetOptions } from '@nrwl/web'; - /* * Babel preset to provide React support for Nx. */ -interface NxReactBabelOptions extends NxWebBabelPresetOptions { +interface NxReactBabelOptions { runtime?: string; importSource?: string; + useBuiltIns?: boolean | string; + decorators?: { + decoratorsBeforeExport?: boolean; + legacy?: boolean; + }; + classProperties?: { + loose?: boolean; + }; } module.exports = function (api: any, options: NxReactBabelOptions) { @@ -17,7 +23,7 @@ module.exports = function (api: any, options: NxReactBabelOptions) { */ const isNextJs = api.caller((caller) => caller?.pagesDir); - const presets: any[] = [['@nrwl/web/babel', options]]; + const presets: any[] = [['@nrwl/js/babel', options]]; /** * Next.js already includes the preset-react, and including it diff --git a/packages/rollup/src/executors/rollup/rollup.impl.ts b/packages/rollup/src/executors/rollup/rollup.impl.ts index 84d0608c96a52..f56f08353382a 100644 --- a/packages/rollup/src/executors/rollup/rollup.impl.ts +++ b/packages/rollup/src/executors/rollup/rollup.impl.ts @@ -238,7 +238,7 @@ export function createRollupOptions( useSwc && swc(), useBabel && getBabelInputPlugin({ - // Let's `@nrwl/web/babel` preset know that we are packaging. + // Lets `@nrwl/js/babel` preset know that we are packaging. caller: { // @ts-ignore // Ignoring type checks for caller since we have custom attributes diff --git a/packages/web/babel.ts b/packages/web/babel.ts index 6ff992d615d0d..df03c9b82253e 100644 --- a/packages/web/babel.ts +++ b/packages/web/babel.ts @@ -1,110 +1,9 @@ -import { dirname } from 'path'; +const nxJsBabelPreset = require('@nrwl/js/babel'); -/* - * Babel preset to provide TypeScript support and module/nomodule for Nx. - */ - -export interface NxWebBabelPresetOptions { - useBuiltIns?: boolean | string; - decorators?: { - decoratorsBeforeExport?: boolean; - legacy?: boolean; - }; - classProperties?: { - loose?: boolean; - }; -} - -module.exports = function (api: any, options: NxWebBabelPresetOptions = {}) { - api.assertVersion(7); - - const isModern = api.caller((caller) => caller?.isModern); - - // This is set by `@nrwl/web:rollup` executor - const isNxPackage = api.caller((caller) => caller?.isNxPackage); - - const emitDecoratorMetadata = api.caller( - (caller) => caller?.emitDecoratorMetadata ?? true +/** @deprecated Use `@nrwl/js/babel`. */ +module.exports = function (api: any, options: any = {}) { + console.warn( + '`@nrwl/web/babel` has been deprecated. Use `@nrwl/js/babel` instead in your .babelrc files.' ); - - // Determine settings for `@babel/plugin-proposal-class-properties`, - // so that we can sync the `loose` option with `@babel/preset-env`. - const classProperties = options.classProperties ?? { loose: true }; - - return { - presets: [ - // Support module/nomodule pattern. - [ - require.resolve('@babel/preset-env'), - // For Jest tests, NODE_ENV is set as 'test' and we only want to set target as Node. - // All other options will fail in Jest since Node does not support some ES features - // such as import syntax. - process.env.NODE_ENV === 'test' - ? { targets: { node: 'current' }, loose: true } - : { - // Allow importing core-js in entrypoint and use browserlist to select polyfills. - useBuiltIns: options.useBuiltIns ?? 'entry', - corejs: 3, - // Do not transform modules to CJS - modules: false, - targets: isModern ? { esmodules: 'intersect' } : undefined, - bugfixes: true, - // Exclude transforms that make all code slower - exclude: ['transform-typeof-symbol'], - // This must match the setting for `@babel/plugin-proposal-class-properties` - loose: classProperties.loose, - }, - ], - [ - require.resolve('@babel/preset-typescript'), - { - allowDeclareFields: true, - }, - ], - ], - plugins: [ - !isNxPackage - ? [ - require.resolve('@babel/plugin-transform-runtime'), - { - corejs: false, - helpers: true, - regenerator: true, - useESModules: isModern, - absoluteRuntime: dirname( - require.resolve('@babel/runtime/package.json') - ), - }, - ] - : null, - require.resolve('babel-plugin-macros'), - emitDecoratorMetadata - ? require.resolve('babel-plugin-transform-typescript-metadata') - : undefined, - // Must use legacy decorators to remain compatible with TypeScript. - [ - require.resolve('@babel/plugin-proposal-decorators'), - options.decorators ?? { legacy: true }, - ], - [ - require.resolve('@babel/plugin-proposal-class-properties'), - classProperties, - ], - ].filter(Boolean), - overrides: [ - // Convert `const enum` to `enum`. The former cannot be supported by babel - // but at least we can get it to not error out. - { - test: /\.tsx?$/, - plugins: [ - [ - require.resolve('babel-plugin-const-enum'), - { - transform: 'removeConst', - }, - ], - ], - }, - ], - }; + return nxJsBabelPreset(api, options); }; diff --git a/packages/web/index.ts b/packages/web/index.ts index 2daa4dc3651b6..5bdcea824fe08 100644 --- a/packages/web/index.ts +++ b/packages/web/index.ts @@ -1,3 +1,2 @@ export { webInitGenerator } from './src/generators/init/init'; export { applicationGenerator } from './src/generators/application/application'; -export type { NxWebBabelPresetOptions } from './babel'; diff --git a/packages/web/migrations.json b/packages/web/migrations.json index 6c03ade81a24d..951c473103823 100644 --- a/packages/web/migrations.json +++ b/packages/web/migrations.json @@ -47,6 +47,12 @@ "version": "15.0.0-beta.1", "description": "Update usages of rollup executors to @nrwl/rollup", "factory": "./src/migrations/update-15-0-0/update-rollup-executor" + }, + "update-babel-preset": { + "cli": "nx", + "version": "15.5.4-beta.0", + "description": "Update `@nrwl/web/babel` preset to `@nrwl/js/babel` for projects that have a .babelrc file.", + "factory": "./src/migrations/update-15-5-4/update-babel-preset" } }, "packageJsonUpdates": { diff --git a/packages/web/package.json b/packages/web/package.json index 1c24a16fe52a4..00d7db2171a7e 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -30,13 +30,6 @@ "migrations": "./migrations.json" }, "dependencies": { - "@babel/core": "^7.15.0", - "@babel/plugin-proposal-class-properties": "^7.14.5", - "@babel/plugin-proposal-decorators": "^7.14.5", - "@babel/plugin-transform-runtime": "^7.15.0", - "@babel/preset-env": "^7.15.0", - "@babel/preset-typescript": "^7.15.0", - "@babel/runtime": "^7.14.8", "@nrwl/cypress": "file:../cypress", "@nrwl/devkit": "file:../devkit", "@nrwl/jest": "file:../jest", @@ -44,9 +37,6 @@ "@nrwl/linter": "file:../linter", "@nrwl/rollup": "file:../rollup", "@nrwl/workspace": "file:../workspace", - "babel-plugin-const-enum": "^1.0.1", - "babel-plugin-macros": "^2.8.0", - "babel-plugin-transform-typescript-metadata": "^0.3.1", "chalk": "^4.1.0", "chokidar": "^3.5.1", "http-server": "^14.1.0", diff --git a/packages/web/src/generators/application/files/app-vite/.babelrc__tmpl__ b/packages/web/src/generators/application/files/app-vite/.babelrc__tmpl__ index 2e593427f7ba2..fb3fd65a5ee01 100644 --- a/packages/web/src/generators/application/files/app-vite/.babelrc__tmpl__ +++ b/packages/web/src/generators/application/files/app-vite/.babelrc__tmpl__ @@ -1,5 +1,5 @@ { "presets": [ - "@nrwl/web/babel" + "@nrwl/js/babel" ] } diff --git a/packages/web/src/generators/application/files/app-webpack/.babelrc__tmpl__ b/packages/web/src/generators/application/files/app-webpack/.babelrc__tmpl__ index 2e593427f7ba2..fb3fd65a5ee01 100644 --- a/packages/web/src/generators/application/files/app-webpack/.babelrc__tmpl__ +++ b/packages/web/src/generators/application/files/app-webpack/.babelrc__tmpl__ @@ -1,5 +1,5 @@ { "presets": [ - "@nrwl/web/babel" + "@nrwl/js/babel" ] } diff --git a/packages/web/src/migrations/update-15-5-4/update-babel-preset.spec.ts b/packages/web/src/migrations/update-15-5-4/update-babel-preset.spec.ts new file mode 100644 index 0000000000000..e7059e7a40630 --- /dev/null +++ b/packages/web/src/migrations/update-15-5-4/update-babel-preset.spec.ts @@ -0,0 +1,67 @@ +import { + addProjectConfiguration, + readJson, + Tree, + writeJson, +} from '@nrwl/devkit'; +import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; + +import update from './update-babel-preset'; + +describe('update-babel-preset', () => { + let tree: Tree; + + beforeEach(() => { + tree = createTreeWithEmptyWorkspace(); + }); + + it('should update preset with options', async () => { + addProjectConfiguration(tree, 'demo', { + root: 'demo', + }); + writeJson(tree, 'demo/.babelrc', { + presets: [ + '@acme/foo', + [ + '@nrwl/web/babel', + { + useBuiltIns: 'usage', + }, + ], + '@acme/bar', + ], + }); + + await update(tree); + + const result = readJson(tree, 'demo/.babelrc'); + expect(result).toEqual({ + presets: [ + '@acme/foo', + [ + '@nrwl/js/babel', + { + useBuiltIns: 'usage', + }, + ], + '@acme/bar', + ], + }); + }); + + it('should update preset without options', async () => { + addProjectConfiguration(tree, 'demo', { + root: 'demo', + }); + writeJson(tree, 'demo/.babelrc', { + presets: ['@acme/foo', '@nrwl/web/babel', '@acme/bar'], + }); + + await update(tree); + + const result = readJson(tree, 'demo/.babelrc'); + expect(result).toEqual({ + presets: ['@acme/foo', '@nrwl/js/babel', '@acme/bar'], + }); + }); +}); diff --git a/packages/web/src/migrations/update-15-5-4/update-babel-preset.ts b/packages/web/src/migrations/update-15-5-4/update-babel-preset.ts new file mode 100644 index 0000000000000..bf7ec9b9520bc --- /dev/null +++ b/packages/web/src/migrations/update-15-5-4/update-babel-preset.ts @@ -0,0 +1,47 @@ +import { + readJson, + writeJson, + Tree, + addDependenciesToPackageJson, + getProjects, + joinPathFragments, +} from '@nrwl/devkit'; +import { nxVersion } from '../../utils/versions'; + +/* Updates @nrwl/web/babel to @nrwl/js/babel because web package is no longer necessary to use webpack/rollup + babel. */ +export default async function update(tree: Tree) { + // Add `@nrwl/js` in case it was missing before. + addDependenciesToPackageJson( + tree, + {}, + { + '@nrwl/js': nxVersion, + } + ); + + const projects = getProjects(tree); + + projects.forEach((config, name) => { + const babelrcPath = joinPathFragments(config.root, '.babelrc'); + + if (!tree.exists(babelrcPath)) return; + + const babelrc = readJson(tree, babelrcPath); + const idx = babelrc?.presets?.findIndex((p) => + typeof p === 'string' + ? p === '@nrwl/web/babel' + : p[0] === '@nrwl/web/babel' + ); + + if (idx === -1) return; + + const preset = babelrc.presets[idx]; + if (typeof preset === 'string') { + babelrc.presets.splice(idx, 1, '@nrwl/js/babel'); + } else if (Array.isArray(preset)) { + babelrc.presets.splice(idx, 1, ['@nrwl/js/babel', preset[1]]); + } + + writeJson(tree, babelrcPath, babelrc); + }); +} diff --git a/packages/workspace/src/generators/library/files/lib/__dot__babelrc__tmpl__ b/packages/workspace/src/generators/library/files/lib/__dot__babelrc__tmpl__ index cf7ddd99c615a..9cbf9798bc1e9 100644 --- a/packages/workspace/src/generators/library/files/lib/__dot__babelrc__tmpl__ +++ b/packages/workspace/src/generators/library/files/lib/__dot__babelrc__tmpl__ @@ -1,3 +1,3 @@ { - "presets": [["@nrwl/web/babel", { "useBuiltIns": "usage" }]] + "presets": [["@nrwl/js/babel", { "useBuiltIns": "usage" }]] }