feat(react): add tailwind as style prompt option for app gen (#21784)

This commit is contained in:
Colum Ferry 2024-02-14 17:51:37 +00:00 committed by GitHub
parent 343c0f6690
commit 3b384c78ac
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 138 additions and 4 deletions

View File

@ -59,6 +59,10 @@
"value": "less", "value": "less",
"label": "LESS [ https://lesscss.org ]" "label": "LESS [ https://lesscss.org ]"
}, },
{
"value": "tailwind",
"label": "tailwind [ https://tailwindcss.com/ ]"
},
{ {
"value": "styled-components", "value": "styled-components",
"label": "styled-components [ https://styled-components.com ]" "label": "styled-components [ https://styled-components.com ]"

View File

@ -46,6 +46,10 @@
"value": "less", "value": "less",
"label": "LESS [ https://lesscss.org ]" "label": "LESS [ https://lesscss.org ]"
}, },
{
"value": "tailwind",
"label": "tailwind [ https://tailwindcss.com/ ]"
},
{ {
"value": "styled-components", "value": "styled-components",
"label": "styled-components [ https://styled-components.com ]" "label": "styled-components [ https://styled-components.com ]"

View File

@ -52,6 +52,10 @@
"value": "less", "value": "less",
"label": "LESS [ https://lesscss.org ]" "label": "LESS [ https://lesscss.org ]"
}, },
{
"value": "tailwind",
"label": "tailwind [ https://tailwindcss.com/ ]"
},
{ {
"value": "styled-components", "value": "styled-components",
"label": "styled-components [ https://styled-components.com ]" "label": "styled-components [ https://styled-components.com ]"

View File

@ -124,7 +124,6 @@ describe('React Applications', () => {
}); });
}, 500000); }, 500000);
// TODO(crystal, @jaysoo): Investigate why this is failing.
it('should be able to use Vite to build and test apps', async () => { it('should be able to use Vite to build and test apps', async () => {
const appName = uniq('app'); const appName = uniq('app');
const libName = uniq('lib'); const libName = uniq('lib');
@ -307,6 +306,48 @@ describe('React Applications', () => {
expect(e2eResults).toContain('All specs passed!'); expect(e2eResults).toContain('All specs passed!');
} }
}, 250_000); }, 250_000);
it('should support tailwind', async () => {
const appName = uniq('app');
runCLI(
`generate @nx/react:app ${appName} --style=tailwind --bundler=vite --no-interactive --skipFormat`
);
// update app to use styled-jsx
updateFile(
`apps/${appName}/src/app/app.tsx`,
`
import NxWelcome from './nx-welcome';
export function App() {
return (
<div className="w-20 h-20">
<NxWelcome title="${appName}" />
</div>
);
}
export default App;
`
);
runCLI(`build ${appName}`);
const outputAssetFiles = listFiles(`dist/apps/${appName}/assets`);
const styleFile = outputAssetFiles.find((filename) =>
filename.endsWith('.css')
);
if (!styleFile) {
throw new Error('Could not find bundled css file');
}
const styleFileContents = readFile(
`dist/apps/${appName}/assets/${styleFile}`
);
const isStyleFileUsingTWClasses =
styleFileContents.includes('w-20') &&
styleFileContents.includes('h-20');
expect(isStyleFileUsingTWClasses).toBeTruthy();
}, 250_000);
}); });
describe('--format', () => { describe('--format', () => {

View File

@ -592,6 +592,10 @@ async function determineReactOptions(
name: 'less', name: 'less',
message: 'LESS [ https://lesscss.org ]', message: 'LESS [ https://lesscss.org ]',
}, },
{
name: 'tailwind',
message: 'tailwind [ https://tailwindcss.com ]',
},
{ {
name: 'styled-components', name: 'styled-components',
message: message:

View File

@ -341,6 +341,21 @@ describe('app', () => {
}); });
}); });
describe('--style tailwind', () => {
it('should generate tailwind setup', async () => {
await applicationGenerator(appTree, { ...schema, style: 'tailwind' });
expect(appTree.exists('my-app/tailwind.config.js')).toEqual(true);
expect(appTree.read('my-app/src/styles.css', 'utf-8'))
.toMatchInlineSnapshot(`
"@tailwind base;
@tailwind components;
@tailwind utilities;
/* You can add global styles to this file, and also import other style files */
"
`);
});
});
it('should setup jest with tsx support', async () => { it('should setup jest with tsx support', async () => {
await applicationGenerator(appTree, { ...schema, name: 'my-app' }); await applicationGenerator(appTree, { ...schema, name: 'my-app' });

View File

@ -40,6 +40,7 @@ import {
} from '@nx/eslint/src/generators/utils/eslint-file'; } from '@nx/eslint/src/generators/utils/eslint-file';
import { initGenerator as jsInitGenerator } from '@nx/js'; import { initGenerator as jsInitGenerator } from '@nx/js';
import { logShowProjectCommand } from '@nx/devkit/src/utils/log-show-project-command'; import { logShowProjectCommand } from '@nx/devkit/src/utils/log-show-project-command';
import { setupTailwindGenerator } from '../setup-tailwind/setup-tailwind';
async function addLinting(host: Tree, options: NormalizedSchema) { async function addLinting(host: Tree, options: NormalizedSchema) {
const tasks: GeneratorCallback[] = []; const tasks: GeneratorCallback[] = [];
@ -133,6 +134,13 @@ export async function applicationGeneratorInternal(
createApplicationFiles(host, options); createApplicationFiles(host, options);
addProject(host, options); addProject(host, options);
if (options.style === 'tailwind') {
const twTask = await setupTailwindGenerator(host, {
project: options.projectName,
});
tasks.push(twTask);
}
if (options.bundler === 'vite') { if (options.bundler === 'vite') {
const { createOrEditViteConfig, viteConfigurationGenerator } = const { createOrEditViteConfig, viteConfigurationGenerator } =
ensurePackage<typeof import('@nx/vite')>('@nx/vite', nxVersion); ensurePackage<typeof import('@nx/vite')>('@nx/vite', nxVersion);

View File

@ -7,7 +7,7 @@
<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" />
<% if (!styledModule && style !== 'none') { %><link rel="stylesheet" href="/src/styles.<%= style %>" /><% } %> <% if (!styledModule && style !== 'none') { %><link rel="stylesheet" href="/src/styles.<%= style === 'tailwind' ? 'css' : style %>" /><% } %>
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View File

@ -0,0 +1,33 @@
<% if (classComponent) { %>
import { Component } from 'react';
<% } if (!minimal) { %>
import NxWelcome from "./nx-welcome";
<% } %>
<% if (classComponent) { %>
export class App extends Component {
render() {
<% } else { %>
export function App() {
<% } %>
return (
<div>
<% if (!minimal) { %>
<NxWelcome title="<%= projectName %>"/>
<% } else { %>
<h1>
<span> Hello there, </span>
Welcome <%= projectName %> 👋
</h1>
<% } %>
</div>);
<% if (classComponent) { %>
}
}
<% } else { %>
}
<% } %>
export default App;
<% if (inSourceTests === true) { %> <%- inSourceVitestTests %> <% } %>

View File

@ -0,0 +1 @@
/* You can add global styles to this file, and also import other style files */

View File

@ -24,6 +24,8 @@ export function createApplicationFiles(host: Tree, options: NormalizedSchema) {
styleSolutionSpecificAppFiles = '../files/style-styled-module'; styleSolutionSpecificAppFiles = '../files/style-styled-module';
} else if (options.style === 'styled-jsx') { } else if (options.style === 'styled-jsx') {
styleSolutionSpecificAppFiles = '../files/style-styled-jsx'; styleSolutionSpecificAppFiles = '../files/style-styled-jsx';
} else if (options.style === 'tailwind') {
styleSolutionSpecificAppFiles = '../files/style-tailwind';
} else if (options.style === 'none') { } else if (options.style === 'none') {
styleSolutionSpecificAppFiles = '../files/style-none'; styleSolutionSpecificAppFiles = '../files/style-none';
} else if (options.globalCss) { } else if (options.globalCss) {
@ -185,6 +187,10 @@ function createNxWebpackPluginOptions(
styles: styles:
options.styledModule || !options.hasStyles options.styledModule || !options.hasStyles
? [] ? []
: [`./src/styles.${options.style}`], : [
`./src/styles.${
options.style !== 'tailwind' ? options.style : 'css'
}`,
],
}; };
} }

View File

@ -46,7 +46,7 @@ export async function normalizeOptions<T extends Schema = Schema>(
const fileName = options.pascalCaseFiles ? 'App' : 'app'; const fileName = options.pascalCaseFiles ? 'App' : 'app';
const styledModule = /^(css|scss|less|none)$/.test(options.style) const styledModule = /^(css|scss|less|tailwind|none)$/.test(options.style)
? null ? null
: options.style; : options.style;

View File

@ -62,6 +62,10 @@
"value": "less", "value": "less",
"label": "LESS [ https://lesscss.org ]" "label": "LESS [ https://lesscss.org ]"
}, },
{
"value": "tailwind",
"label": "tailwind [ https://tailwindcss.com/ ]"
},
{ {
"value": "styled-components", "value": "styled-components",
"label": "styled-components [ https://styled-components.com ]" "label": "styled-components [ https://styled-components.com ]"

View File

@ -49,6 +49,10 @@
"value": "less", "value": "less",
"label": "LESS [ https://lesscss.org ]" "label": "LESS [ https://lesscss.org ]"
}, },
{
"value": "tailwind",
"label": "tailwind [ https://tailwindcss.com/ ]"
},
{ {
"value": "styled-components", "value": "styled-components",
"label": "styled-components [ https://styled-components.com ]" "label": "styled-components [ https://styled-components.com ]"

View File

@ -55,6 +55,10 @@
"value": "less", "value": "less",
"label": "LESS [ https://lesscss.org ]" "label": "LESS [ https://lesscss.org ]"
}, },
{
"value": "tailwind",
"label": "tailwind [ https://tailwindcss.com/ ]"
},
{ {
"value": "styled-components", "value": "styled-components",
"label": "styled-components [ https://styled-components.com ]" "label": "styled-components [ https://styled-components.com ]"

View File

@ -2,6 +2,7 @@ const VALID_STYLES = [
'css', 'css',
'scss', 'scss',
'less', 'less',
'tailwind',
'styled-components', 'styled-components',
'@emotion/styled', '@emotion/styled',
'styled-jsx', 'styled-jsx',

View File

@ -2,6 +2,7 @@ export type SupportedStyles =
| 'css' | 'css'
| 'scss' | 'scss'
| 'less' | 'less'
| 'tailwind'
| 'styled-components' | 'styled-components'
| '@emotion/styled' | '@emotion/styled'
| 'styled-jsx' | 'styled-jsx'