diff --git a/packages/react/migrations.json b/packages/react/migrations.json index f163b910b5..63c3499fdf 100644 --- a/packages/react/migrations.json +++ b/packages/react/migrations.json @@ -92,6 +92,12 @@ "version": "12.0.0-beta.0", "description": "Remove @types/react-redux from package.json since react-redux installs the package automatically now", "factory": "./src/migrations/update-12-0-0/remove-react-redux-types-package" + }, + "update-emotion-setup-13.0.0": { + "cli": "nx", + "version": "13.0.0-beta.0", + "description": "Update tsconfig.json to use `jsxImportSource` to support css prop", + "factory": "./src/migrations/update-13-0-0/update-emotion-setup" } }, "packageJsonUpdates": { diff --git a/packages/react/src/generators/application/application.spec.ts b/packages/react/src/generators/application/application.spec.ts index 9370c04a04..662bfa0c8f 100644 --- a/packages/react/src/generators/application/application.spec.ts +++ b/packages/react/src/generators/application/application.spec.ts @@ -537,6 +537,18 @@ Object { expect(content).toContain(''); }); + it('should add jsxImportSource to tsconfig.json', async () => { + await applicationGenerator(appTree, { + ...schema, + style: '@emotion/styled', + }); + + const tsconfigJson = readJson(appTree, 'apps/my-app/tsconfig.json'); + expect(tsconfigJson.compilerOptions['jsxImportSource']).toEqual( + '@emotion/react' + ); + }); + it('should exclude styles from workspace.json', async () => { await applicationGenerator(appTree, { ...schema, diff --git a/packages/react/src/generators/application/files/common/tsconfig.json__tmpl__ b/packages/react/src/generators/application/files/common/tsconfig.json__tmpl__ index 074c6682dc..877486407b 100644 --- a/packages/react/src/generators/application/files/common/tsconfig.json__tmpl__ +++ b/packages/react/src/generators/application/files/common/tsconfig.json__tmpl__ @@ -2,6 +2,7 @@ "extends": "<%= offsetFromRoot %>tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", + <% if (style === '@emotion/styled') { %>"jsxImportSource": "@emotion/react",<% } %> "allowJs": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true diff --git a/packages/react/src/generators/library/files/lib/tsconfig.json__tmpl__ b/packages/react/src/generators/library/files/lib/tsconfig.json__tmpl__ index 514efa55d7..1e7db70dbf 100644 --- a/packages/react/src/generators/library/files/lib/tsconfig.json__tmpl__ +++ b/packages/react/src/generators/library/files/lib/tsconfig.json__tmpl__ @@ -2,6 +2,7 @@ "extends": "<%= offsetFromRoot %>tsconfig.base.json", "compilerOptions": { "jsx": "react-jsx", + <% if (style === '@emotion/styled') { %>"jsxImportSource": "@emotion/react",<% } %> "allowJs": true, "esModuleInterop": true, "allowSyntheticDefaultImports": true diff --git a/packages/react/src/generators/library/library.spec.ts b/packages/react/src/generators/library/library.spec.ts index 351b09981c..5375e02d2d 100644 --- a/packages/react/src/generators/library/library.spec.ts +++ b/packages/react/src/generators/library/library.spec.ts @@ -488,6 +488,7 @@ describe('lib', () => { const workspaceJson = readJson(appTree, '/workspace.json'); const babelrc = readJson(appTree, 'libs/my-lib/.babelrc'); + const tsconfigJson = readJson(appTree, 'libs/my-lib/tsconfig.json'); expect(workspaceJson.projects['my-lib'].architect.build).toMatchObject({ options: { @@ -495,6 +496,9 @@ describe('lib', () => { }, }); expect(babelrc.plugins).toEqual(['@emotion/babel-plugin']); + expect(tsconfigJson.compilerOptions['jsxImportSource']).toEqual( + '@emotion/react' + ); }); it('should support styled-jsx', async () => { diff --git a/packages/react/src/migrations/update-13-0-0/update-emotion-setup.spec.ts b/packages/react/src/migrations/update-13-0-0/update-emotion-setup.spec.ts new file mode 100644 index 0000000000..4024a34dd9 --- /dev/null +++ b/packages/react/src/migrations/update-13-0-0/update-emotion-setup.spec.ts @@ -0,0 +1,90 @@ +import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing'; +import { readJson, Tree } from '@nrwl/devkit'; +import { updateEmotionSetup } from './update-emotion-setup'; + +describe('Update tsconfig config for Emotion', () => { + let tree: Tree; + + beforeEach(() => { + tree = createTreeWithEmptyWorkspace(); + }); + + it(`should add jsxImportSource if it uses @emotion/react`, async () => { + tree.write( + 'workspace.json', + JSON.stringify({ + projects: { + 'no-emotion-app': { + root: 'apps/no-emotion-app', + projectType: 'application', + }, + 'plain-react-app': { + root: 'apps/plain-react-app', + projectType: 'application', + }, + 'emotion-app': { + root: 'apps/emotion-app', + projectType: 'application', + }, + }, + }) + ); + tree.write( + 'nx.json', + JSON.stringify({ + projects: { + 'no-emotion-app': {}, + 'plain-react-app': {}, + 'emotion-app': {}, + }, + }) + ); + tree.write('apps/no-emotion-app/.babelrc', JSON.stringify({})); + tree.write( + 'apps/no-emotion-app/tsconfig.json', + JSON.stringify({ compilerOptions: {} }) + ); + tree.write( + 'apps/plain-react-app/.babelrc', + JSON.stringify({ + presets: ['@nrwl/react/babel'], + plugins: [], + }) + ); + tree.write( + 'apps/plain-react-app/tsconfig.json', + JSON.stringify({ compilerOptions: { jsx: 'react-jsx' } }) + ); + tree.write( + 'apps/emotion-app/.babelrc', + JSON.stringify({ + presets: [ + [ + '@nrwl/react/babel', + { + runtime: 'automatic', + importSource: '@emotion/react', + }, + ], + ], + plugins: ['@emotion/babel-plugin'], + }) + ); + tree.write( + 'apps/emotion-app/tsconfig.json', + JSON.stringify({ compilerOptions: { jsx: 'react-jsx' } }) + ); + + await updateEmotionSetup(tree); + + expect(readJson(tree, 'apps/no-emotion-app/tsconfig.json')).toEqual({ + compilerOptions: {}, + }); + expect(readJson(tree, 'apps/plain-react-app/tsconfig.json')).toEqual({ + compilerOptions: { jsx: 'react-jsx' }, + }); + expect(readJson(tree, 'apps/emotion-app/tsconfig.json')).toEqual({ + compilerOptions: { jsx: 'react-jsx', jsxImportSource: '@emotion/react' }, + }); + }); +}); diff --git a/packages/react/src/migrations/update-13-0-0/update-emotion-setup.ts b/packages/react/src/migrations/update-13-0-0/update-emotion-setup.ts new file mode 100644 index 0000000000..7978f7dd91 --- /dev/null +++ b/packages/react/src/migrations/update-13-0-0/update-emotion-setup.ts @@ -0,0 +1,42 @@ +import { + formatFiles, + getProjects, + Tree, + readJson, + updateJson, +} from '@nrwl/devkit'; + +export async function updateEmotionSetup(host: Tree) { + const projects = getProjects(host); + + projects.forEach((p) => { + let hasEmotion = false; + const babelrcPath = `${p.root}/.babelrc`; + const tsConfigPath = `${p.root}/tsconfig.json`; + + if (host.exists(babelrcPath)) { + const babelrc = readJson(host, babelrcPath); + if (babelrc.presets) { + for (const [idx, preset] of babelrc.presets.entries()) { + if (Array.isArray(preset)) { + if (!preset[0].includes('@nrwl/react/babel')) continue; + const emotionOptions = preset[1]; + hasEmotion = emotionOptions.importSource === '@emotion/react'; + break; + } + } + } + } + + if (hasEmotion && host.exists(tsConfigPath)) { + updateJson(host, tsConfigPath, (json) => { + json.compilerOptions.jsxImportSource = '@emotion/react'; + return json; + }); + } + }); + + await formatFiles(host); +} + +export default updateEmotionSetup;