fix(react): add missing style preprocessors when using Vite (#13600)

This commit is contained in:
Jack Hsu 2022-12-02 16:56:23 -05:00 committed by GitHub
parent 3e2b8d987f
commit 67c7822ad3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 195 additions and 17 deletions

View File

@ -124,6 +124,12 @@
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "Skip generating a vite config file" "description": "Skip generating a vite config file"
},
"coverageProvider": {
"type": "string",
"enum": ["c8", "istanbul"],
"default": "c8",
"description": "Coverage provider to use."
} }
}, },
"required": ["project"], "required": ["project"],

View File

@ -970,7 +970,8 @@ describe('app', () => {
describe('setup React app with --bundler=vite', () => { describe('setup React app with --bundler=vite', () => {
let viteAppTree: Tree; let viteAppTree: Tree;
beforeAll(async () => {
beforeEach(async () => {
viteAppTree = createTreeWithEmptyV1Workspace(); viteAppTree = createTreeWithEmptyV1Workspace();
await applicationGenerator(viteAppTree, { ...schema, bundler: 'vite' }); await applicationGenerator(viteAppTree, { ...schema, bundler: 'vite' });
}); });
@ -984,6 +985,7 @@ describe('app', () => {
buildTarget: 'my-app:build', buildTarget: 'my-app:build',
}); });
}); });
it('should add dependencies in package.json', () => { it('should add dependencies in package.json', () => {
const packageJson = readJson(viteAppTree, '/package.json'); const packageJson = readJson(viteAppTree, '/package.json');
@ -1020,6 +1022,30 @@ describe('app', () => {
viteAppTree.exists('/apps/insourceTests/src/app/app.spec.tsx') viteAppTree.exists('/apps/insourceTests/src/app/app.spec.tsx')
).toBe(false); ).toBe(false);
}); });
it.each`
style | pkg
${'less'} | ${'less'}
${'scss'} | ${'sass'}
${'styl'} | ${'stylus'}
`(
'should add style preprocessor when vite is used',
async ({ style, pkg }) => {
await applicationGenerator(viteAppTree, {
...schema,
style,
bundler: 'vite',
unitTestRunner: 'vitest',
name: style,
});
expect(readJson(viteAppTree, 'package.json')).toMatchObject({
devDependencies: {
[pkg]: expect.any(String),
},
});
}
);
}); });
describe('setting generator defaults', () => { describe('setting generator defaults', () => {

View File

@ -31,6 +31,7 @@ import {
swcCoreVersion, swcCoreVersion,
swcLoaderVersion, swcLoaderVersion,
} from '../../utils/versions'; } from '../../utils/versions';
import { installCommonDependencies } from './lib/install-common-dependencies';
async function addLinting(host: Tree, options: NormalizedSchema) { async function addLinting(host: Tree, options: NormalizedSchema) {
const tasks: GeneratorCallback[] = []; const tasks: GeneratorCallback[] = [];
@ -122,6 +123,7 @@ export async function applicationGenerator(host: Tree, schema: Schema) {
const vitestTask = await vitestGenerator(host, { const vitestTask = await vitestGenerator(host, {
uiFramework: 'react', uiFramework: 'react',
coverageProvider: 'c8',
project: options.projectName, project: options.projectName,
inSourceTests: options.inSourceTests, inSourceTests: options.inSourceTests,
}); });
@ -153,6 +155,8 @@ export async function applicationGenerator(host: Tree, schema: Schema) {
// Handle tsconfig.spec.json for jest or vitest // Handle tsconfig.spec.json for jest or vitest
updateSpecConfig(host, options); updateSpecConfig(host, options);
const stylePreprocessorTask = installCommonDependencies(host, options);
tasks.push(stylePreprocessorTask);
const styledTask = addStyledModuleDependencies(host, options.styledModule); const styledTask = addStyledModuleDependencies(host, options.styledModule);
tasks.push(styledTask); tasks.push(styledTask);
const routingTask = addRouting(host, options); const routingTask = addRouting(host, options);

View File

@ -0,0 +1,35 @@
import { addDependenciesToPackageJson, Tree } from '@nrwl/devkit';
import {
lessVersion,
sassVersion,
stylusVersion,
} from '../../../utils/versions';
import { NormalizedSchema } from '../schema';
export function installCommonDependencies(
host: Tree,
options: NormalizedSchema
) {
let devDependencies = null;
// Vite requires style preprocessors to be installed manually.
// `@nrwl/webpack` installs them automatically for now.
// TODO(jack): Once we clean up webpack we can remove this check
if (options.bundler === 'vite' || options.unitTestRunner === 'vitest') {
switch (options.style) {
case 'scss':
devDependencies = { sass: sassVersion };
break;
case 'less':
devDependencies = { less: lessVersion };
break;
case 'styl':
devDependencies = { stylus: stylusVersion };
break;
}
}
return devDependencies
? addDependenciesToPackageJson(host, {}, devDependencies)
: function noop() {};
}

View File

@ -312,6 +312,7 @@ describe('component', () => {
.read('libs/my-lib/src/lib/hello/hello.tsx') .read('libs/my-lib/src/lib/hello/hello.tsx')
.toString(); .toString();
expect(content).toContain('<style jsx>'); expect(content).toContain('<style jsx>');
expect(content).not.toContain("styles['container']");
}); });
it('should add dependencies to package.json', async () => { it('should add dependencies to package.json', async () => {

View File

@ -14,7 +14,7 @@ import { Route, Link } from 'react-router-dom';
import styled from '<%= styledModule %>'; import styled from '<%= styledModule %>';
<% } else { <% } else {
var wrapper = 'div'; var wrapper = 'div';
var extras = globalCss ? '' : " className={styles['container']}"; var extras = globalCss || styledModule === 'styled-jsx' ? '' : " className={styles['container']}";
%> %>
<%- style !== 'styled-jsx' ? globalCss ? `import './${fileName}.${style}';` : `import styles from './${fileName}.module.${style}';`: '' %> <%- style !== 'styled-jsx' ? globalCss ? `import './${fileName}.${style}';` : `import styles from './${fileName}.module.${style}';`: '' %>
<% } <% }

View File

@ -0,0 +1,44 @@
import { addDependenciesToPackageJson, Tree } from '@nrwl/devkit';
import {
lessVersion,
reactDomVersion,
reactVersion,
sassVersion,
stylusVersion,
swcCoreVersion,
} from '../../../utils/versions';
import { NormalizedSchema } from '../schema';
export function installCommonDependencies(
host: Tree,
options: NormalizedSchema
) {
const devDependencies =
options.compiler === 'swc' ? { '@swc/core': swcCoreVersion } : {};
// Vite requires style preprocessors to be installed manually.
// `@nrwl/webpack` installs them automatically for now.
// TODO(jack): Once we clean up webpack we can remove this check
if (options.bundler === 'vite' || options.unitTestRunner === 'vitest') {
switch (options.style) {
case 'scss':
devDependencies['sass'] = sassVersion;
break;
case 'less':
devDependencies['less'] = lessVersion;
break;
case 'styl':
devDependencies['stylus'] = stylusVersion;
break;
}
}
return addDependenciesToPackageJson(
host,
{
react: reactVersion,
'react-dom': reactDomVersion,
},
devDependencies
);
}

View File

@ -771,4 +771,28 @@ describe('lib', () => {
}).not.toThrow(); }).not.toThrow();
} }
); );
it.each`
style | pkg
${'less'} | ${'less'}
${'scss'} | ${'sass'}
${'styl'} | ${'stylus'}
`(
'should add style preprocessor when vite is used',
async ({ style, pkg }) => {
await libraryGenerator(tree, {
...defaultSchema,
style,
bundler: 'vite',
unitTestRunner: 'vitest',
name: 'myLib',
});
expect(readJson(tree, 'package.json')).toMatchObject({
devDependencies: {
[pkg]: expect.any(String),
},
});
}
);
}); });

View File

@ -1,5 +1,4 @@
import { import {
addDependenciesToPackageJson,
addProjectConfiguration, addProjectConfiguration,
convertNxGenerator, convertNxGenerator,
ensurePackage, ensurePackage,
@ -11,12 +10,7 @@ import {
} from '@nrwl/devkit'; } from '@nrwl/devkit';
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial'; import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
import { import { nxVersion } from '../../utils/versions';
nxVersion,
reactDomVersion,
reactVersion,
swcCoreVersion,
} from '../../utils/versions';
import componentGenerator from '../component/component'; import componentGenerator from '../component/component';
import initGenerator from '../init/init'; import initGenerator from '../init/init';
import { Schema } from './schema'; import { Schema } from './schema';
@ -27,6 +21,7 @@ import { addLinting } from './lib/add-linting';
import { updateAppRoutes } from './lib/update-app-routes'; import { updateAppRoutes } from './lib/update-app-routes';
import { createFiles } from './lib/create-files'; import { createFiles } from './lib/create-files';
import { updateBaseTsConfig } from './lib/update-base-tsconfig'; import { updateBaseTsConfig } from './lib/update-base-tsconfig';
import { installCommonDependencies } from './lib/install-common-dependencies';
export async function libraryGenerator(host: Tree, schema: Schema) { export async function libraryGenerator(host: Tree, schema: Schema) {
const tasks: GeneratorCallback[] = []; const tasks: GeneratorCallback[] = [];
@ -123,6 +118,7 @@ export async function libraryGenerator(host: Tree, schema: Schema) {
const vitestTask = await vitestGenerator(host, { const vitestTask = await vitestGenerator(host, {
uiFramework: 'react', uiFramework: 'react',
project: options.name, project: options.name,
coverageProvider: 'c8',
inSourceTests: options.inSourceTests, inSourceTests: options.inSourceTests,
}); });
tasks.push(vitestTask); tasks.push(vitestTask);
@ -153,14 +149,7 @@ export async function libraryGenerator(host: Tree, schema: Schema) {
} }
if (!options.skipPackageJson) { if (!options.skipPackageJson) {
const installReactTask = await addDependenciesToPackageJson( const installReactTask = await installCommonDependencies(host, options);
host,
{
react: reactVersion,
'react-dom': reactDomVersion,
},
options.compiler === 'swc' ? { '@swc/core': swcCoreVersion } : {}
);
tasks.push(installReactTask); tasks.push(installReactTask);
} }

View File

@ -52,3 +52,8 @@ export const isbotVersion = '^3.6.5';
export const corsVersion = '~2.8.5'; export const corsVersion = '~2.8.5';
export const typesCorsVersion = '~2.8.12'; export const typesCorsVersion = '~2.8.12';
export const moduleFederationNodeVersion = '~0.9.6'; export const moduleFederationNodeVersion = '~0.9.6';
// style preprocessors
export const lessVersion = '3.12.2';
export const sassVersion = '^1.55.0';
export const stylusVersion = '^0.55.0';

View File

@ -59,6 +59,7 @@ export async function viteConfigurationGenerator(tree: Tree, schema: Schema) {
project: schema.project, project: schema.project,
uiFramework: schema.uiFramework, uiFramework: schema.uiFramework,
inSourceTests: schema.inSourceTests, inSourceTests: schema.inSourceTests,
coverageProvider: 'c8',
skipViteConfig: true, skipViteConfig: true,
}); });
tasks.push(vitestTask); tasks.push(vitestTask);

View File

@ -1,6 +1,7 @@
export interface VitestGeneratorSchema { export interface VitestGeneratorSchema {
project: string; project: string;
uiFramework: 'react' | 'none'; uiFramework: 'react' | 'none';
coverageProvider: 'c8' | 'istanbul';
inSourceTests?: boolean; inSourceTests?: boolean;
skipViteConfig?: boolean; skipViteConfig?: boolean;
} }

View File

@ -26,6 +26,12 @@
"type": "boolean", "type": "boolean",
"default": false, "default": false,
"description": "Skip generating a vite config file" "description": "Skip generating a vite config file"
},
"coverageProvider": {
"type": "string",
"enum": ["c8", "istanbul"],
"default": "c8",
"description": "Coverage provider to use."
} }
}, },
"required": ["project"] "required": ["project"]

View File

@ -1,4 +1,5 @@
import { import {
addDependenciesToPackageJson,
convertNxGenerator, convertNxGenerator,
formatFiles, formatFiles,
generateFiles, generateFiles,
@ -18,6 +19,10 @@ import { VitestGeneratorSchema } from './schema';
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial'; import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
import initGenerator from '../init/init'; import initGenerator from '../init/init';
import {
vitestCoverageC8Version,
vitestCoverageIstanbulVersion,
} from '../../utils/versions';
export async function vitestGenerator( export async function vitestGenerator(
tree: Tree, tree: Tree,
@ -45,6 +50,22 @@ export async function vitestGenerator(
createFiles(tree, schema, root); createFiles(tree, schema, root);
updateTsConfig(tree, schema, root); updateTsConfig(tree, schema, root);
const installCoverageProviderTask = addDependenciesToPackageJson(
tree,
{},
schema.coverageProvider === 'istanbul'
? {
'@vitest/coverage-istanbul': vitestCoverageIstanbulVersion,
}
: {
'@vitest/coverage-c8': vitestCoverageC8Version,
}
);
tasks.push(installCoverageProviderTask);
if (schema.coverageProvider === 'istanbul') {
}
await formatFiles(tree); await formatFiles(tree);
return runTasksInSerial(...tasks); return runTasksInSerial(...tasks);

View File

@ -10,6 +10,7 @@ describe('vitest generator', () => {
const options: VitestGeneratorSchema = { const options: VitestGeneratorSchema = {
project: 'my-test-react-app', project: 'my-test-react-app',
uiFramework: 'react', uiFramework: 'react',
coverageProvider: 'c8',
}; };
beforeEach(async () => { beforeEach(async () => {
@ -126,6 +127,9 @@ describe('vitest generator', () => {
test: { test: {
globals: true, globals: true,
cache: {
dir: '../../node_modules/.vitest'
},
environment: 'jsdom', environment: 'jsdom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
@ -169,6 +173,9 @@ describe('vitest generator', () => {
}, },
test: { test: {
globals: true, globals: true,
cache: {
dir: '../../node_modules/.vitest'
},
environment: 'jsdom', environment: 'jsdom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
includeSource: ['src/**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'] includeSource: ['src/**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}']

View File

@ -354,6 +354,9 @@ export function writeViteConfig(tree: Tree, options: Schema) {
const testOption = options.includeVitest const testOption = options.includeVitest
? `test: { ? `test: {
globals: true, globals: true,
cache: {
dir: '${offsetFromRoot(projectConfig.root)}node_modules/.vitest'
},
environment: 'jsdom', environment: 'jsdom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
${ ${

View File

@ -9,3 +9,7 @@ export const vitePluginVueJsxVersion = '^2.1.1';
export const viteTsConfigPathsVersion = '^3.5.2'; export const viteTsConfigPathsVersion = '^3.5.2';
export const jsdomVersion = '~20.0.3'; export const jsdomVersion = '~20.0.3';
export const vitePluginDtsVersion = '~1.7.1'; export const vitePluginDtsVersion = '~1.7.1';
// Coverage providers
export const vitestCoverageC8Version = '~0.25.3';
export const vitestCoverageIstanbulVersion = '~0.25.3';

View File

@ -220,6 +220,7 @@ export async function applicationGenerator(host: Tree, schema: Schema) {
const vitestTask = await vitestGenerator(host, { const vitestTask = await vitestGenerator(host, {
uiFramework: 'none', uiFramework: 'none',
project: options.projectName, project: options.projectName,
coverageProvider: 'c8',
inSourceTests: options.inSourceTests, inSourceTests: options.inSourceTests,
}); });
tasks.push(vitestTask); tasks.push(vitestTask);