fix(nextjs): update babel setup to better support next apps (#4944)

* fix(nextjs): update babel setup to better support next apps

* fix(testing): remove babel-jest.config.json used in jest.config.js

- Fix `@nrwl/web/babel` to support Jest as well
This commit is contained in:
Jack Hsu 2021-03-09 16:32:36 -05:00 committed by GitHub
parent 372b7939c6
commit ddec362a5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 378 additions and 265 deletions

View File

@ -90,6 +90,14 @@ Type: `boolean`
Use pascal case file names. Use pascal case file names.
### skipBabelrc
Default: `false`
Type: `boolean`
Do not generate .babelrc file. Useful for Node libraries that are not compiled by Babel
### skipFormat ### skipFormat
Default: `false` Default: `false`

View File

@ -90,6 +90,14 @@ Type: `boolean`
Use pascal case file names. Use pascal case file names.
### skipBabelrc
Default: `false`
Type: `boolean`
Do not generate .babelrc file. Useful for Node libraries that are not compiled by Babel
### skipFormat ### skipFormat
Default: `false` Default: `false`

View File

@ -90,6 +90,14 @@ Type: `boolean`
Use pascal case file names. Use pascal case file names.
### skipBabelrc
Default: `false`
Type: `boolean`
Do not generate .babelrc file. Useful for Node libraries that are not compiled by Babel
### skipFormat ### skipFormat
Default: `false` Default: `false`

View File

@ -4,6 +4,9 @@
module.exports = function (api, options) { module.exports = function (api, options) {
api.assertVersion(7); api.assertVersion(7);
return { return {
presets: [[require.resolve('babel-preset-gatsby'), { useBuiltIns: true }]], presets: [
'@nrwl/web/babel',
[require.resolve('babel-preset-gatsby'), { useBuiltIns: true }],
],
}; };
}; };

View File

@ -181,13 +181,7 @@ describe('app', () => {
.read('apps/my-app/src/pages/index.tsx') .read('apps/my-app/src/pages/index.tsx')
.toString(); .toString();
const babelJestConfig = readJson(
tree,
'apps/my-app/babel-jest.config.json'
);
expect(indexContent).toMatch(/<style jsx>/); expect(indexContent).toMatch(/<style jsx>/);
expect(babelJestConfig.plugins).toContain('styled-jsx/babel');
expect( expect(
tree.exists('apps/my-app/src/pages/index.module.styled-jsx') tree.exists('apps/my-app/src/pages/index.module.styled-jsx')
).toBeFalsy(); ).toBeFalsy();

View File

@ -1,12 +1,11 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`jestProject --babelJest should generate proper jest.transform and babel-jest.config.json when babelJest and supportTsx is true 1`] = ` exports[`jestProject --babelJest should generate proper jest.transform when babelJest and supportTsx is true 1`] = `
"module.exports = { "module.exports = {
displayName: 'lib1', displayName: 'lib1',
preset: '../../jest.preset.js', preset: '../../jest.preset.js',
transform: { transform: {
'^.+\\\\\\\\.[tj]sx?$': [ 'babel-jest', '^.+\\\\\\\\.[tj]sx?$': 'babel-jest'
{ cwd: __dirname, configFile: './babel-jest.config.json' }]
}, },
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/libs/lib1' coverageDirectory: '../../coverage/libs/lib1'
@ -14,13 +13,12 @@ exports[`jestProject --babelJest should generate proper jest.transform and babel
" "
`; `;
exports[`jestProject --babelJest should generate proper jest.transform and babel-jest.config.json when babelJest is true 1`] = ` exports[`jestProject --babelJest should generate proper jest.transform when babelJest is true 1`] = `
"module.exports = { "module.exports = {
displayName: 'lib1', displayName: 'lib1',
preset: '../../jest.preset.js', preset: '../../jest.preset.js',
transform: { transform: {
'^.+\\\\\\\\.[tj]s$': [ 'babel-jest', '^.+\\\\\\\\.[tj]s$': 'babel-jest'
{ cwd: __dirname, configFile: './babel-jest.config.json' }]
}, },
moduleFileExtensions: ['ts', 'js', 'html'], moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../../coverage/libs/lib1' coverageDirectory: '../../coverage/libs/lib1'

View File

@ -1,14 +0,0 @@
{
"presets": [
[
"@babel/preset-env",
{
"targets": {
"node": "current"
}
}
],
"@babel/preset-typescript"<% if (supportTsx) { %>,
"@babel/preset-react"<% } %>
]
}

View File

@ -16,8 +16,7 @@ module.exports = {
},<% } %><% if(testEnvironment) { %> },<% } %><% if(testEnvironment) { %>
testEnvironment: '<%= testEnvironment %>',<% } %><% if(skipSerializers){ %> testEnvironment: '<%= testEnvironment %>',<% } %><% if(skipSerializers){ %>
transform: { transform: {
<% if (supportTsx){ %>'^.+\\.[tj]sx?$'<% } else { %>'^.+\\.[tj]s$'<% } %>: <% if (transformer == 'babel-jest') { %>[ 'babel-jest', <% if (supportTsx){ %>'^.+\\.[tj]sx?$'<% } else { %>'^.+\\.[tj]s$'<% } %>: <% if (transformer == 'babel-jest') { %>'babel-jest'<% } else { %> 'ts-jest' <% } %>
{ cwd: __dirname, configFile: './babel-jest.config.json' }]<% } else { %> 'ts-jest' <% } %>
},<% if (supportTsx) { %> },<% if (supportTsx) { %>
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],<% } else { %> moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],<% } else { %>
moduleFileExtensions: ['ts', 'js', 'html'],<% } %><% } %> moduleFileExtensions: ['ts', 'js', 'html'],<% } %><% } %>

View File

@ -275,7 +275,7 @@ describe('jestProject', () => {
}); });
}); });
it('should generate proper jest.transform and babel-jest.config.json when babelJest is true', async () => { it('should generate proper jest.transform when babelJest is true', async () => {
await jestProjectGenerator(tree, { await jestProjectGenerator(tree, {
...defaultOptions, ...defaultOptions,
project: 'lib1', project: 'lib1',
@ -291,27 +291,9 @@ describe('jestProject', () => {
expect( expect(
tree.read('libs/lib1/jest.config.js').toString() tree.read('libs/lib1/jest.config.js').toString()
).toMatchSnapshot(); ).toMatchSnapshot();
expect(tree.exists('libs/lib1/babel-jest.config.json'));
expect(readJson(tree, 'libs/lib1/babel-jest.config.json'))
.toMatchInlineSnapshot(`
Object {
"presets": Array [
Array [
"@babel/preset-env",
Object {
"targets": Object {
"node": "current",
},
},
],
"@babel/preset-typescript",
],
}
`);
}); });
it('should generate proper jest.transform and babel-jest.config.json when babelJest and supportTsx is true', async () => { it('should generate proper jest.transform when babelJest and supportTsx is true', async () => {
await jestProjectGenerator(tree, { await jestProjectGenerator(tree, {
...defaultOptions, ...defaultOptions,
project: 'lib1', project: 'lib1',
@ -321,24 +303,6 @@ describe('jestProject', () => {
expect( expect(
tree.read('libs/lib1/jest.config.js').toString() tree.read('libs/lib1/jest.config.js').toString()
).toMatchSnapshot(); ).toMatchSnapshot();
expect(readJson(tree, 'libs/lib1/babel-jest.config.json'))
.toMatchInlineSnapshot(`
Object {
"presets": Array [
Array [
"@babel/preset-env",
Object {
"targets": Object {
"node": "current",
},
},
],
"@babel/preset-typescript",
"@babel/preset-react",
],
}
`);
}); });
}); });
}); });

View File

@ -21,8 +21,4 @@ export function createFiles(tree: Tree, options: JestProjectSchema) {
if (options.setupFile === 'none') { if (options.setupFile === 'none') {
tree.delete(join(projectConfig.root, './src/test-setup.ts')); tree.delete(join(projectConfig.root, './src/test-setup.ts'));
} }
if (!options.babelJest) {
tree.delete(join(projectConfig.root, './babel-jest.config.json'));
}
} }

View File

@ -6,6 +6,7 @@ import { serializeJson } from '@nrwl/workspace';
import { jestConfigObject } from '../utils/config/legacy/functions'; import { jestConfigObject } from '../utils/config/legacy/functions';
import { getJestObject } from './require-jest-config'; import { getJestObject } from './require-jest-config';
jest.mock('./require-jest-config'); jest.mock('./require-jest-config');
const getJestObjectMock = getJestObject as jest.Mock<typeof getJestObject>; const getJestObjectMock = getJestObject as jest.Mock<typeof getJestObject>;
@ -28,10 +29,7 @@ const reactJestObject = {
preset: '../../jest.config.js', preset: '../../jest.config.js',
transform: { transform: {
'^(?!.*\\\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest', '^(?!.*\\\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest',
'^.+\\\\.[tj]sx?$': [ '^.+\\\\.[tj]sx?$': ['babel-jest', { cwd: __dirname }],
'babel-jest',
{ cwd: __dirname, configFile: './babel-jest.config.json' },
],
}, },
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
coverageDirectory: '../../coverage/apps/my-react-app', coverageDirectory: '../../coverage/apps/my-react-app',

10
packages/next/babel.ts Normal file
View File

@ -0,0 +1,10 @@
/*
* Babel preset to provide Next.js support for Nx.
*/
module.exports = function (api, options) {
api.assertVersion(7);
return {
presets: ['next/babel'],
plugins: [['@babel/plugin-proposal-decorators', { legacy: true }]],
};
};

View File

@ -24,6 +24,12 @@
"version": "11.0.0-beta.0", "version": "11.0.0-beta.0",
"description": "Update libraries", "description": "Update libraries",
"factory": "./src/migrations/update-11-0-0/update-11-0-0" "factory": "./src/migrations/update-11-0-0/update-11-0-0"
},
"update-babel-config-11.5.0": {
"cli": "nx",
"version": "11.5.0-beta.1",
"description": "Update .babelrc to use '@nrwl/next/babel' instead of 'next/babel'",
"factory": "./src/migrations/update-11-5-0/update-babel-config"
} }
}, },
"packageJsonUpdates": { "packageJsonUpdates": {

View File

@ -32,6 +32,7 @@
"next": "^10.0.1" "next": "^10.0.1"
}, },
"dependencies": { "dependencies": {
"@babel/plugin-proposal-decorators": "^7.8.3",
"@nrwl/react": "*", "@nrwl/react": "*",
"@nrwl/cypress": "*", "@nrwl/cypress": "*",
"@nrwl/jest": "*", "@nrwl/jest": "*",

View File

@ -28,12 +28,19 @@ function withNx(nextConfig = {} as any) {
const userWebpack = nextConfig.webpack || ((x) => x); const userWebpack = nextConfig.webpack || ((x) => x);
return { return {
...nextConfig, ...nextConfig,
webpack: (config, options) => {
/*
* Update babel to support our monorepo setup.
* The 'upward' mode allows the root babel.config.json and per-project .babelrc files to be picked up.
*/
options.defaultLoaders.babel.options.babelrc = true;
options.defaultLoaders.babel.options.rootMode = 'upward';
/* /*
* Modify the Next.js webpack config to allow workspace libs to use css modules. * Modify the Next.js webpack config to allow workspace libs to use css modules.
*
* Note: This would be easier if Next.js exposes css-loader and sass-loader on `defaultLoaders`. * Note: This would be easier if Next.js exposes css-loader and sass-loader on `defaultLoaders`.
*/ */
webpack: (config, options) => {
// Include workspace libs in css/sass loaders // Include workspace libs in css/sass loaders
const includes = [join(appRootPath, workspaceLayout().libsDir)]; const includes = [join(appRootPath, workspaceLayout().libsDir)];

View File

@ -150,13 +150,7 @@ describe('app', () => {
const indexContent = tree.read('apps/my-app/pages/index.tsx').toString(); const indexContent = tree.read('apps/my-app/pages/index.tsx').toString();
const babelJestConfig = readJson(
tree,
'apps/my-app/babel-jest.config.json'
);
expect(indexContent).toMatch(/<style jsx>{`.page {}`}<\/style>/); expect(indexContent).toMatch(/<style jsx>{`.page {}`}<\/style>/);
expect(babelJestConfig.plugins).toContain('styled-jsx/babel');
expect( expect(
tree.exists('apps/my-app/pages/index.module.styled-jsx') tree.exists('apps/my-app/pages/index.module.styled-jsx')
).toBeFalsy(); ).toBeFalsy();

View File

@ -1,4 +1,6 @@
{ {
"presets": ["next/babel"], "presets": ["@nrwl/next/babel"],
"plugins": [<% if (style === 'styled-components') { %>["styled-components", { "pure": true, "ssr": true }]<% } %>] "plugins": [
<% if (style === 'styled-components') { %>["styled-components", { "pure": true, "ssr": true }]<% } %>
]
} }

View File

@ -1,6 +1,5 @@
import { NormalizedSchema } from './normalize-options'; import { NormalizedSchema } from './normalize-options';
import { Tree } from '@nrwl/devkit'; import { Tree } from '@nrwl/devkit';
import { updateBabelJestConfig } from '@nrwl/react/src/rules/update-babel-jest-config';
export function updateJestConfig(host: Tree, options: NormalizedSchema) { export function updateJestConfig(host: Tree, options: NormalizedSchema) {
if (options.unitTestRunner !== 'jest') { if (options.unitTestRunner !== 'jest') {
@ -14,11 +13,4 @@ export function updateJestConfig(host: Tree, options: NormalizedSchema) {
"transform: {\n '^(?!.*\\\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest'," "transform: {\n '^(?!.*\\\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest',"
); );
host.write(configPath, content); host.write(configPath, content);
updateBabelJestConfig(host, options.appProjectRoot, (json) => {
if (options.style === 'styled-jsx') {
json.plugins = (json.plugins || []).concat('styled-jsx/babel');
}
return json;
});
} }

View File

@ -0,0 +1,63 @@
import * as path from 'path';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { readJson, Tree } from '@nrwl/devkit';
import updateBabelConfig from './update-babel-config';
describe('Migrate babel setup', () => {
let tree: Tree;
beforeEach(async () => {
tree = createTreeWithEmptyWorkspace();
});
it(`should add web babel preset if it does not exist`, async () => {
tree.write(
'workspace.json',
JSON.stringify({
projects: {
app1: {
root: 'apps/app1',
},
app2: {
root: 'apps/app2',
},
app3: {
root: 'apps/app3',
},
},
})
);
tree.write(
'nx.json',
JSON.stringify({
projects: {
app1: {},
app2: {},
app3: {},
},
})
);
tree.write(
'apps/app1/.babelrc',
JSON.stringify({
presets: ['@nrwl/react/babel'],
})
);
tree.write(
'apps/app2/.babelrc',
JSON.stringify({ presets: ['next/babel'] })
);
await updateBabelConfig(tree);
expect(readJson(tree, 'apps/app1/.babelrc')).toMatchObject({
presets: ['@nrwl/react/babel'],
});
expect(readJson(tree, 'apps/app2/.babelrc')).toMatchObject({
presets: ['@nrwl/next/babel'],
});
expect(tree.exists('apps/app3/.babelrc')).not.toBeTruthy();
});
});

View File

@ -0,0 +1,22 @@
import { formatFiles, getProjects, Tree, updateJson } from '@nrwl/devkit';
export async function updateBabelConfig(host: Tree) {
const projects = getProjects(host);
projects.forEach((p) => {
const babelrcPath = `${p.root}/.babelrc`;
if (!host.exists(babelrcPath)) return;
updateJson(host, babelrcPath, (json) => {
json.presets = json.presets || [];
json.presets = json.presets.map((x) =>
x === 'next/babel' ? '@nrwl/next/babel' : x
);
return json;
});
});
await formatFiles(host);
}
export default updateBabelConfig;

View File

@ -337,33 +337,13 @@ describe('app', () => {
displayName: 'my-node-app', displayName: 'my-node-app',
preset: '../../jest.preset.js', preset: '../../jest.preset.js',
transform: { transform: {
'^.+\\\\\\\\.[tj]s$': [ '^.+\\\\\\\\.[tj]s$': 'babel-jest',
'babel-jest',
{ cwd: __dirname, configFile: './babel-jest.config.json' },
],
}, },
moduleFileExtensions: ['ts', 'js', 'html'], moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../../coverage/apps/my-node-app', coverageDirectory: '../../coverage/apps/my-node-app',
}; };
" "
`); `);
expect(readJsonInTree(tree, 'apps/my-node-app/babel-jest.config.json'))
.toMatchInlineSnapshot(`
Object {
"presets": Array [
Array [
"@babel/preset-env",
Object {
"targets": Object {
"node": "current",
},
},
],
"@babel/preset-typescript",
],
}
`);
}); });
}); });
describe('--js flag', () => { describe('--js flag', () => {

View File

@ -449,34 +449,13 @@ describe('lib', () => {
displayName: 'my-lib', displayName: 'my-lib',
preset: '../../jest.preset.js', preset: '../../jest.preset.js',
transform: { transform: {
'^.+\\\\\\\\.[tj]sx?$': [ '^.+\\\\\\\\.[tj]sx?$': 'babel-jest',
'babel-jest',
{ cwd: __dirname, configFile: './babel-jest.config.json' },
],
}, },
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/libs/my-lib', coverageDirectory: '../../coverage/libs/my-lib',
}; };
" "
`); `);
expect(readJsonInTree(tree, 'libs/my-lib/babel-jest.config.json'))
.toMatchInlineSnapshot(`
Object {
"presets": Array [
Array [
"@babel/preset-env",
Object {
"targets": Object {
"node": "current",
},
},
],
"@babel/preset-typescript",
"@babel/preset-react",
],
}
`);
}); });
}); });
describe('--js flag', () => { describe('--js flag', () => {

View File

@ -5,6 +5,9 @@
module.exports = function (api: any, options: {}) { module.exports = function (api: any, options: {}) {
api.assertVersion(7); api.assertVersion(7);
return { return {
presets: [[require.resolve('@babel/preset-react'), { useBuiltIns: true }]], presets: [
'@nrwl/web/babel',
[require.resolve('@babel/preset-react'), { useBuiltIns: true }],
],
}; };
}; };

View File

@ -565,12 +565,7 @@ describe('app', () => {
}); });
const babelrc = readJson(appTree, 'apps/my-app/.babelrc'); const babelrc = readJson(appTree, 'apps/my-app/.babelrc');
const babelJestConfig = readJson(
appTree,
'apps/my-app/babel-jest.config.json'
);
expect(babelrc.plugins).toContain('styled-jsx/babel'); expect(babelrc.plugins).toContain('styled-jsx/babel');
expect(babelJestConfig.plugins).toContain('styled-jsx/babel');
}); });
}); });

View File

@ -1,4 +1,3 @@
import { updateBabelJestConfig } from '../../../rules/update-babel-jest-config';
import { updateJestConfigContent } from '../../../utils/jest-utils'; import { updateJestConfigContent } from '../../../utils/jest-utils';
import { NormalizedSchema } from '../schema'; import { NormalizedSchema } from '../schema';
import { offsetFromRoot, Tree, updateJson } from '@nrwl/devkit'; import { offsetFromRoot, Tree, updateJson } from '@nrwl/devkit';
@ -26,11 +25,4 @@ export function updateJestConfig(host: Tree, options: NormalizedSchema) {
const originalContent = host.read(configPath).toString(); const originalContent = host.read(configPath).toString();
const content = updateJestConfigContent(originalContent); const content = updateJestConfigContent(originalContent);
host.write(configPath, content); host.write(configPath, content);
updateBabelJestConfig(host, options.appProjectRoot, (json) => {
if (options.style === 'styled-jsx') {
json.plugins = (json.plugins || []).concat('styled-jsx/babel');
}
return json;
});
} }

View File

@ -487,10 +487,6 @@ describe('lib', () => {
const workspaceJson = readJson(appTree, '/workspace.json'); const workspaceJson = readJson(appTree, '/workspace.json');
const babelrc = readJson(appTree, 'libs/my-lib/.babelrc'); const babelrc = readJson(appTree, 'libs/my-lib/.babelrc');
const babelJestConfig = readJson(
appTree,
'libs/my-lib/babel-jest.config.json'
);
expect(workspaceJson.projects['my-lib'].architect.build).toMatchObject({ expect(workspaceJson.projects['my-lib'].architect.build).toMatchObject({
options: { options: {
@ -498,7 +494,6 @@ describe('lib', () => {
}, },
}); });
expect(babelrc.plugins).toContain('styled-jsx/babel'); expect(babelrc.plugins).toContain('styled-jsx/babel');
expect(babelJestConfig.plugins).toContain('styled-jsx/babel');
}); });
it('should support style none', async () => { it('should support style none', async () => {

View File

@ -19,7 +19,6 @@ import {
typesReactRouterDomVersion, typesReactRouterDomVersion,
} from '../../utils/versions'; } from '../../utils/versions';
import { Schema } from './schema'; import { Schema } from './schema';
import { updateBabelJestConfig } from '../../rules/update-babel-jest-config';
import { import {
addDependenciesToPackageJson, addDependenciesToPackageJson,
addProjectConfiguration, addProjectConfiguration,
@ -95,13 +94,6 @@ export async function libraryGenerator(host: Tree, schema: Schema) {
babelJest: true, babelJest: true,
}); });
tasks.push(jestTask); tasks.push(jestTask);
updateBabelJestConfig(host, options.projectRoot, (json) => {
if (options.style === 'styled-jsx') {
json.plugins = (json.plugins || []).concat('styled-jsx/babel');
}
return json;
});
} }
if (options.component) { if (options.component) {

View File

@ -1,31 +0,0 @@
import { readJson, Tree } from '@nrwl/devkit';
import { updateBabelJestConfig } from './update-babel-jest-config';
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
describe('updateBabelJestConfig', () => {
let tree: Tree;
beforeEach(() => {
tree = createTreeWithEmptyWorkspace();
});
it('should update babel-jest.config.json', async () => {
tree.write('/apps/demo/babel-jest.config.json', JSON.stringify({}));
updateBabelJestConfig(tree, '/apps/demo', (json) => {
json.plugins = ['test'];
return json;
});
const config = readJson(tree, '/apps/demo/babel-jest.config.json');
expect(config.plugins).toEqual(['test']);
});
it('should do nothing if project does not use babel jest', async () => {
updateBabelJestConfig(tree, '/apps/demo', (json) => {
json.plugins = ['test'];
return json;
});
expect(tree.exists('/apps/demo/babel-jest.config.json')).toBe(false);
});
});

View File

@ -1,14 +0,0 @@
import { Tree, updateJson } from '@nrwl/devkit';
type BabelJestConfigUpdater<T> = (json: T) => T;
export function updateBabelJestConfig<T = any>(
host: Tree,
projectRoot: string,
update: BabelJestConfigUpdater<T>
) {
const configPath = `${projectRoot}/babel-jest.config.json`;
if (host.exists(configPath)) {
updateJson(host, configPath, update);
}
}

View File

@ -25,7 +25,12 @@ module.exports = function (api: any, options: NxReactBabelPresetOptions = {}) {
// Support module/nomodule pattern. // Support module/nomodule pattern.
[ [
require.resolve('@babel/preset-env'), 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' } }
: {
// Allow importing core-js in entrypoint and use browserlist to select polyfills. // Allow importing core-js in entrypoint and use browserlist to select polyfills.
// This is needed for differential loading as well. // This is needed for differential loading as well.
useBuiltIns: 'entry', useBuiltIns: 'entry',

View File

@ -14,6 +14,12 @@
"version": "9.2.0-beta.1", "version": "9.2.0-beta.1",
"description": "Set buildLibsFromSource property to true to not break existing projects.", "description": "Set buildLibsFromSource property to true to not break existing projects.",
"factory": "./src/migrations/update-9-2-0/set-build-libs-from-source" "factory": "./src/migrations/update-9-2-0/set-build-libs-from-source"
},
"update-babel-config-11.5.0": {
"cli": "nx",
"version": "11.5.0-beta.1",
"description": "Update babel config to support different TS setup",
"factory": "./src/migrations/update-11-5-0/update-babel-config"
} }
} }
} }

View File

@ -354,31 +354,13 @@ describe('app', () => {
preset: '../../jest.preset.js', preset: '../../jest.preset.js',
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'], setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
transform: { transform: {
'^.+\\\\\\\\.[tj]s$': [ 'babel-jest', '^.+\\\\\\\\.[tj]s$': 'babel-jest'
{ cwd: __dirname, configFile: './babel-jest.config.json' }]
}, },
moduleFileExtensions: ['ts', 'js', 'html'], moduleFileExtensions: ['ts', 'js', 'html'],
coverageDirectory: '../../coverage/apps/my-app' coverageDirectory: '../../coverage/apps/my-app'
}; };
" "
`); `);
expect(readJson(tree, 'apps/my-app/babel-jest.config.json'))
.toMatchInlineSnapshot(`
Object {
"presets": Array [
Array [
"@babel/preset-env",
Object {
"targets": Object {
"node": "current",
},
},
],
"@babel/preset-typescript",
],
}
`);
}); });
}); });
}); });

View File

@ -1 +1,5 @@
{} {
"presets": [
"@nrwl/web/babel"
]
}

View File

@ -42,7 +42,6 @@ function initRootBabelConfig(tree: Tree) {
} }
writeJson(tree, '/babel.config.json', { writeJson(tree, '/babel.config.json', {
presets: ['@nrwl/web/babel'],
babelrcRoots: ['*'], // Make sure .babelrc files other than root can be loaded in a monorepo babelrcRoots: ['*'], // Make sure .babelrc files other than root can be loaded in a monorepo
}); });
} }

View File

@ -0,0 +1,104 @@
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
import { readJson, Tree } from '@nrwl/devkit';
import { updateBabelConfig } from './update-babel-config';
describe('Migrate babel setup', () => {
let tree: Tree;
beforeEach(async () => {
tree = createTreeWithEmptyWorkspace();
});
it(`should add web babel preset if it does not exist`, async () => {
tree.write(
'workspace.json',
JSON.stringify({
projects: {
app1: {
root: 'apps/app1',
projectType: 'application',
},
app2: {
root: 'apps/app2',
projectType: 'application',
},
app3: {
root: 'apps/app3',
projectType: 'application',
},
app4: {
root: 'apps/app4',
projectType: 'application',
},
app5: {
root: 'apps/app5',
projectType: 'application',
},
lib1: {
root: 'libs/lib1',
projectType: 'library',
},
},
})
);
tree.write(
'nx.json',
JSON.stringify({
projects: {
app1: {},
app2: {},
app3: {},
app4: {},
app5: {},
lib1: {},
},
})
);
tree.write(
'babel.config.json',
JSON.stringify({
presets: ['@nrwl/web/babel'],
})
);
tree.write('apps/app1/.babelrc', JSON.stringify({}));
tree.write(
'apps/app2/.babelrc',
JSON.stringify({ presets: ['@nrwl/web/babel'] })
);
tree.write(
'apps/app3/.babelrc',
JSON.stringify({ presets: ['@nrwl/react/babel'] })
);
tree.write(
'apps/app4/.babelrc',
JSON.stringify({ presets: ['@nrwl/gatsby/babel'] })
);
await updateBabelConfig(tree);
expect(readJson(tree, 'babel.config.json').presets).not.toContain(
'@nrwl/web/babel'
);
expect(readJson(tree, 'apps/app1/.babelrc')).toMatchObject({
presets: ['@nrwl/web/babel'],
});
expect(readJson(tree, 'apps/app2/.babelrc')).toMatchObject({
presets: ['@nrwl/web/babel'],
});
expect(readJson(tree, 'apps/app3/.babelrc')).toMatchObject({
presets: ['@nrwl/react/babel'],
});
expect(readJson(tree, 'apps/app4/.babelrc')).toMatchObject({
presets: ['@nrwl/gatsby/babel'],
});
expect(tree.exists('apps/app5/.babelrc')).not.toBeTruthy();
expect(readJson(tree, 'libs/lib1/.babelrc')).toMatchObject({
presets: ['@nrwl/web/babel'],
});
});
});

View File

@ -0,0 +1,49 @@
import { formatFiles, getProjects, Tree, updateJson } from '@nrwl/devkit';
export async function updateBabelConfig(host: Tree) {
const projects = getProjects(host);
if (host.exists('babel.config.json')) {
updateJson(host, 'babel.config.json', (json) => {
if (Array.isArray(json.presets)) {
json.presets = json.presets.filter((x) => x !== '@nrwl/web/babel');
}
return json;
});
}
projects.forEach((p) => {
const babelrcPath = `${p.root}/.babelrc`;
// Add `@nrwl/web/babel` to projects that did not previously use it.
// This is needed because we removed it from the root.
if (host.exists(babelrcPath)) {
updateJson(host, babelrcPath, (json) => {
json.presets = json.presets || [];
if (
-1 ===
json.presets.findIndex(
(x) =>
x === '@nrwl/web/babel' ||
x === '@nrwl/react/babel' ||
x === '@nrwl/gatsby/babel'
)
) {
json.presets.push('@nrwl/web/babel');
}
return json;
});
// Non-buildable libraries might be included in applications that
// require .babelrc to exist and contain '@nrwl/web/babel' preset
} else if (p.projectType === 'library') {
host.write(
babelrcPath,
JSON.stringify({ presets: ['@nrwl/web/babel'] }, null, 2)
);
}
});
await formatFiles(host);
}
export default updateBabelConfig;

View File

@ -0,0 +1,3 @@
{
"presets": ["@nrwl/web/babel"]
}

View File

@ -718,34 +718,16 @@ describe('lib', () => {
displayName: 'my-lib', displayName: 'my-lib',
preset: '../../jest.preset.js', preset: '../../jest.preset.js',
transform: { transform: {
'^.+\\\\\\\\.[tj]sx?$': [ 'babel-jest', '^.+\\\\\\\\.[tj]sx?$': 'babel-jest'
{ cwd: __dirname, configFile: './babel-jest.config.json' }]
}, },
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
coverageDirectory: '../../coverage/libs/my-lib' coverageDirectory: '../../coverage/libs/my-lib'
}; };
" "
`); `);
});
});
expect(readJson(tree, 'libs/my-lib/babel-jest.config.json'))
.toMatchInlineSnapshot(`
Object {
"presets": Array [
Array [
"@babel/preset-env",
Object {
"targets": Object {
"node": "current",
},
},
],
"@babel/preset-typescript",
"@babel/preset-react",
],
}
`);
});
});
describe('--pascalCaseFiles', () => { describe('--pascalCaseFiles', () => {
it('should generate files with upper case names', async () => { it('should generate files with upper case names', async () => {
await libraryGenerator(tree, { await libraryGenerator(tree, {
@ -772,4 +754,23 @@ describe('lib', () => {
).toBeTruthy(); ).toBeTruthy();
}); });
}); });
describe('--skipBabelrc', () => {
it('should skip generating .babelrc when --skipBabelrc=true', async () => {
await libraryGenerator(tree, {
...defaultOptions,
name: 'myLib',
skipBabelrc: true,
});
expect(tree.exists('libs/my-lib/.babelrc')).toBeFalsy();
});
it('should generate .babelrc by default', async () => {
await libraryGenerator(tree, {
...defaultOptions,
name: 'myLib',
});
expect(tree.exists('libs/my-lib/.babelrc')).toBeTruthy();
});
});
}); });

View File

@ -100,6 +100,7 @@ function createFiles(tree: Tree, options: NormalizedSchema) {
generateFiles(tree, join(__dirname, './files/lib'), options.projectRoot, { generateFiles(tree, join(__dirname, './files/lib'), options.projectRoot, {
...options, ...options,
dot: '.',
className, className,
name, name,
propertyName, propertyName,
@ -117,6 +118,10 @@ function createFiles(tree: Tree, options: NormalizedSchema) {
); );
} }
if (options.skipBabelrc) {
tree.delete(join(options.projectRoot, '.babelrc'));
}
if (options.js) { if (options.js) {
toJS(tree); toJS(tree);
} }

View File

@ -15,4 +15,5 @@ export interface Schema {
babelJest?: boolean; babelJest?: boolean;
pascalCaseFiles?: boolean; pascalCaseFiles?: boolean;
strict?: boolean; strict?: boolean;
skipBabelrc?: boolean;
} }

View File

@ -81,6 +81,11 @@
"type": "boolean", "type": "boolean",
"description": "Whether to enable tsconfig strict mode or not.", "description": "Whether to enable tsconfig strict mode or not.",
"default": false "default": false
},
"skipBabelrc": {
"type": "boolean",
"description": "Do not generate .babelrc file. Useful for Node libraries that are not compiled by Babel",
"default": false
} }
}, },
"required": ["name"] "required": ["name"]

View File

@ -49,7 +49,6 @@ describe('preset', () => {
expect(tree.children('apps/proj')).toMatchSnapshot(); expect(tree.children('apps/proj')).toMatchSnapshot();
expect(tree.children('apps/proj/src/')).toMatchSnapshot(); expect(tree.children('apps/proj/src/')).toMatchSnapshot();
expect(tree.children('apps/proj/src/app')).toMatchSnapshot(); expect(tree.children('apps/proj/src/app')).toMatchSnapshot();
console.log(tree.children(''));
expect( expect(
JSON.parse(tree.read('/workspace.json').toString()).cli.defaultCollection JSON.parse(tree.read('/workspace.json').toString()).cli.defaultCollection