fix(react): generate valid Vite + JSX setup for React (#27130)
The current `@nx/react:app` generator does not take the `--js` option into account. There are two problems: 1. `index.html` includes `main.tsx` not `main.jsx`. 2. `.js` files with JSX are invalid in Vite, and must be named `.jsx`. This PR adds a new option to the `toJS` devkit util to preserve `.jsx` rather than renaming them to `.js`. The vast majority of non-Vite React projects will use `.js` and not `.jsx` (e.g. Next.js, Expo, Remix, etc.) so we just want to apply this change to Vite only for now. In the future we could enhance React generators to support `--jsx`, for example. ## Current Behavior <!-- This is the behavior we have today --> ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #20810
This commit is contained in:
parent
d2e3fc79db
commit
45c458e677
@ -4,8 +4,9 @@
|
|||||||
|
|
||||||
#### Type declaration
|
#### Type declaration
|
||||||
|
|
||||||
| Name | Type |
|
| Name | Type |
|
||||||
| :---------- | :------------------------------ |
|
| :----------- | :------------------------------ |
|
||||||
| `extension` | `".js"` \| `".mjs"` \| `".cjs"` |
|
| `extension?` | `".js"` \| `".mjs"` \| `".cjs"` |
|
||||||
| `module?` | `ModuleKind` |
|
| `module?` | `ModuleKind` |
|
||||||
| `target?` | `ScriptTarget` |
|
| `target?` | `ScriptTarget` |
|
||||||
|
| `useJsx?` | `boolean` |
|
||||||
|
|||||||
45
packages/devkit/src/generators/to-js.spec.ts
Normal file
45
packages/devkit/src/generators/to-js.spec.ts
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
import { createTree } from 'nx/src/generators/testing-utils/create-tree';
|
||||||
|
import type { Tree } from 'nx/src/generators/tree';
|
||||||
|
|
||||||
|
import { toJS } from './to-js';
|
||||||
|
|
||||||
|
describe('toJS', () => {
|
||||||
|
let tree: Tree;
|
||||||
|
beforeEach(() => {
|
||||||
|
tree = createTree();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should renamed .ts and .tsx file to .js', () => {
|
||||||
|
tree.write('a.ts', '// a');
|
||||||
|
tree.write('b.tsx', '// b');
|
||||||
|
|
||||||
|
toJS(tree);
|
||||||
|
|
||||||
|
expect(tree.exists('a.ts')).toBeFalsy();
|
||||||
|
expect(tree.exists('b.tsx')).toBeFalsy();
|
||||||
|
expect(tree.read('a.js', 'utf-8')).toContain('// a');
|
||||||
|
expect(tree.read('b.js', 'utf-8')).toContain('// b');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support different extensions', () => {
|
||||||
|
tree.write('a.ts', '// a');
|
||||||
|
|
||||||
|
toJS(tree, {
|
||||||
|
extension: '.mjs',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.read('a.mjs', 'utf-8')).toContain('// a');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should support .jsx rather than .js files (for Vite)', () => {
|
||||||
|
tree.write('a.ts', '// a');
|
||||||
|
tree.write('b.tsx', '// b');
|
||||||
|
|
||||||
|
toJS(tree, {
|
||||||
|
useJsx: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(tree.read('a.js', 'utf-8')).toContain('// a');
|
||||||
|
expect(tree.read('b.jsx', 'utf-8')).toContain('// b');
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,12 +1,13 @@
|
|||||||
import type { Tree } from 'nx/src/devkit-exports';
|
import type { Tree } from 'nx/src/devkit-exports';
|
||||||
import type { ScriptTarget, ModuleKind } from 'typescript';
|
import type { ModuleKind, ScriptTarget } from 'typescript';
|
||||||
import { typescriptVersion } from '../utils/versions';
|
import { typescriptVersion } from '../utils/versions';
|
||||||
import { ensurePackage } from '../utils/package-json';
|
import { ensurePackage } from '../utils/package-json';
|
||||||
|
|
||||||
export type ToJSOptions = {
|
export type ToJSOptions = {
|
||||||
target?: ScriptTarget;
|
extension?: '.js' | '.mjs' | '.cjs';
|
||||||
module?: ModuleKind;
|
module?: ModuleKind;
|
||||||
extension: '.js' | '.mjs' | '.cjs';
|
target?: ScriptTarget;
|
||||||
|
useJsx?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -32,10 +33,15 @@ export function toJS(tree: Tree, options?: ToJSOptions): void {
|
|||||||
module: options?.module ?? ModuleKind.ESNext,
|
module: options?.module ?? ModuleKind.ESNext,
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
tree.rename(
|
tree.rename(c.path, c.path.replace(/\.ts$/, options?.extension ?? '.js'));
|
||||||
c.path,
|
if (options?.useJsx) {
|
||||||
c.path.replace(/\.tsx?$/, options?.extension ?? '.js')
|
tree.rename(c.path, c.path.replace(/\.tsx$/, '.jsx'));
|
||||||
);
|
} else {
|
||||||
|
tree.rename(
|
||||||
|
c.path,
|
||||||
|
c.path.replace(/\.tsx$/, options?.extension ?? '.js')
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -399,6 +399,19 @@ describe('app', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(appTree.read('my-app/vite.config.ts', 'utf-8')).toMatchSnapshot();
|
expect(appTree.read('my-app/vite.config.ts', 'utf-8')).toMatchSnapshot();
|
||||||
|
expect(appTree.read('my-app/index.html', 'utf-8')).toContain('main.tsx');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should setup vite if bundler is vite (--js)', async () => {
|
||||||
|
await applicationGenerator(appTree, {
|
||||||
|
...schema,
|
||||||
|
name: 'my-app',
|
||||||
|
bundler: 'vite',
|
||||||
|
js: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(appTree.read('my-app/index.html', 'utf-8')).toContain('main.jsx');
|
||||||
|
expect(appTree.exists('my-app/src/main.jsx')).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should setup the nx vite dev server builder if bundler is vite', async () => {
|
it('should setup the nx vite dev server builder if bundler is vite', async () => {
|
||||||
|
|||||||
@ -11,6 +11,6 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
<script type="module" src="/src/main.tsx"></script>
|
<script type="module" src="/src/<%= js ? 'main.jsx' : 'main.tsx' %>"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@ -42,6 +42,7 @@ export function createApplicationFiles(host: Tree, options: NormalizedSchema) {
|
|||||||
const templateVariables = {
|
const templateVariables = {
|
||||||
...names(options.name),
|
...names(options.name),
|
||||||
...options,
|
...options,
|
||||||
|
js: !!options.js, // Ensure this is defined in template
|
||||||
tmpl: '',
|
tmpl: '',
|
||||||
offsetFromRoot: offsetFromRoot(options.appProjectRoot),
|
offsetFromRoot: offsetFromRoot(options.appProjectRoot),
|
||||||
appTests,
|
appTests,
|
||||||
@ -155,7 +156,9 @@ export function createApplicationFiles(host: Tree, options: NormalizedSchema) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
if (options.js) {
|
if (options.js) {
|
||||||
toJS(host);
|
toJS(host, {
|
||||||
|
useJsx: options.bundler === 'vite',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
createTsConfig(
|
createTsConfig(
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user