feat(react): add Vite bundler option for buildable libraries (#13382)
This commit is contained in:
parent
c2db462992
commit
a63a25d2e2
@ -48,6 +48,11 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
},
|
},
|
||||||
|
"skipBabelConfig": {
|
||||||
|
"description": "Do not generate a root babel.config.json (if babel is not needed).",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
"js": {
|
"js": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false,
|
"default": false,
|
||||||
@ -244,7 +249,7 @@
|
|||||||
"description": "The bundler to use.",
|
"description": "The bundler to use.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["vite", "webpack"],
|
"enum": ["vite", "webpack"],
|
||||||
"x-prompt": "Which bundler do you want to use?",
|
"x-prompt": "Which bundler do you want to use to build the application?",
|
||||||
"default": "webpack"
|
"default": "webpack"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -339,8 +344,7 @@
|
|||||||
"unitTestRunner": {
|
"unitTestRunner": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["jest", "vitest", "none"],
|
"enum": ["jest", "vitest", "none"],
|
||||||
"description": "Test runner to use for unit tests.",
|
"description": "Test runner to use for unit tests."
|
||||||
"default": "jest"
|
|
||||||
},
|
},
|
||||||
"inSourceTests": {
|
"inSourceTests": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@ -384,7 +388,7 @@
|
|||||||
"buildable": {
|
"buildable": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false,
|
"default": false,
|
||||||
"description": "Generate a buildable library."
|
"description": "Generate a buildable library. If a bundler is set then the library is buildable by default."
|
||||||
},
|
},
|
||||||
"importPath": {
|
"importPath": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -419,11 +423,17 @@
|
|||||||
"description": "Split the project configuration into `<projectRoot>/project.json` rather than including it inside `workspace.json`.",
|
"description": "Split the project configuration into `<projectRoot>/project.json` rather than including it inside `workspace.json`.",
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"bundler": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The bundler to use.",
|
||||||
|
"enum": ["vite", "rollup"],
|
||||||
|
"x-prompt": "Which bundler would you like to use to build the library?"
|
||||||
|
},
|
||||||
"compiler": {
|
"compiler": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["babel", "swc"],
|
"enum": ["babel", "swc"],
|
||||||
"default": "babel",
|
"default": "babel",
|
||||||
"description": "Which compiler to use."
|
"description": "Which compiler to use. Does not apply if bundler is set to Vite."
|
||||||
},
|
},
|
||||||
"skipPackageJson": {
|
"skipPackageJson": {
|
||||||
"description": "Do not add dependencies to `package.json`.",
|
"description": "Do not add dependencies to `package.json`.",
|
||||||
|
|||||||
@ -31,6 +31,11 @@
|
|||||||
"enum": ["react", "none"],
|
"enum": ["react", "none"],
|
||||||
"default": "react",
|
"default": "react",
|
||||||
"x-prompt": "What UI framework plugin should Vite use?"
|
"x-prompt": "What UI framework plugin should Vite use?"
|
||||||
|
},
|
||||||
|
"includeLib": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Add dependencies needed to build libraries.",
|
||||||
|
"default": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"examplesFile": "This is a generator will initialize Vite.js in your workspace. It will install all the necessary dependencies. You can read more about how this generator works, in the [Vite package overview page](/packages/vite).\n\nYou can use it on its own like this:\n\n```bash\nnx g @nrwl/vite:configuration\n```\n\nHowever, this generator will be called when you are either converting an existing React or Web app to use Vite, using the [`@nrwl/vite:configuration` generator](/packages/vite/generators/configuration), or when you are creating a new React or Web app using the [`@nrwl/react:app`](/packages/react/generators/application) or [`@nrwl/web:app`](<(/packages/web/generators/application)>) generators, if you choose `vite` as the `bundler`.\n\n## Examples\n\n### Install all the necessary dependencies for Vite and the React plugin\n\n```bash\nnx g @nrwl/vite:init --uiFramework=react\n```\n\n### Install all the necessary dependencies for Vite\n\n```bash\nnx g @nrwl/vite:init --uiFramework=none\n```\n",
|
"examplesFile": "This is a generator will initialize Vite.js in your workspace. It will install all the necessary dependencies. You can read more about how this generator works, in the [Vite package overview page](/packages/vite).\n\nYou can use it on its own like this:\n\n```bash\nnx g @nrwl/vite:configuration\n```\n\nHowever, this generator will be called when you are either converting an existing React or Web app to use Vite, using the [`@nrwl/vite:configuration` generator](/packages/vite/generators/configuration), or when you are creating a new React or Web app using the [`@nrwl/react:app`](/packages/react/generators/application) or [`@nrwl/web:app`](<(/packages/web/generators/application)>) generators, if you choose `vite` as the `bundler`.\n\n## Examples\n\n### Install all the necessary dependencies for Vite and the React plugin\n\n```bash\nnx g @nrwl/vite:init --uiFramework=react\n```\n\n### Install all the necessary dependencies for Vite\n\n```bash\nnx g @nrwl/vite:init --uiFramework=none\n```\n",
|
||||||
@ -60,6 +65,12 @@
|
|||||||
"x-dropdown": "project",
|
"x-dropdown": "project",
|
||||||
"x-prompt": "What is the name of the project to set up a webpack for?"
|
"x-prompt": "What is the name of the project to set up a webpack for?"
|
||||||
},
|
},
|
||||||
|
"includeLib": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Add a library build option.",
|
||||||
|
"default": false,
|
||||||
|
"x-prompt": "Does this project contain a buildable library?"
|
||||||
|
},
|
||||||
"uiFramework": {
|
"uiFramework": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "UI Framework to use for Vite.",
|
"description": "UI Framework to use for Vite.",
|
||||||
|
|||||||
@ -53,6 +53,11 @@
|
|||||||
"description": "Do not add dependencies to `package.json`.",
|
"description": "Do not add dependencies to `package.json`.",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
|
},
|
||||||
|
"skipBabelConfig": {
|
||||||
|
"description": "Do not generate a root babel.config.json (if babel is not needed).",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [],
|
"required": [],
|
||||||
|
|||||||
@ -97,7 +97,7 @@ describe('Build React libraries and apps', () => {
|
|||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
killPorts();
|
killPorts();
|
||||||
cleanupProject();
|
// cleanupProject();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Buildable libraries', () => {
|
describe('Buildable libraries', () => {
|
||||||
@ -252,4 +252,21 @@ export async function h() { return 'c'; }
|
|||||||
}).toThrow();
|
}).toThrow();
|
||||||
}, 250000);
|
}, 250000);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should support bundling with Vite', async () => {
|
||||||
|
const libName = uniq('lib');
|
||||||
|
|
||||||
|
runCLI(
|
||||||
|
`generate @nrwl/react:lib ${libName} --bundler=vite --no-interactive`
|
||||||
|
);
|
||||||
|
|
||||||
|
await runCLIAsync(`build ${libName}`);
|
||||||
|
|
||||||
|
checkFilesExist(
|
||||||
|
`dist/libs/${libName}/package.json`,
|
||||||
|
`dist/libs/${libName}/index.d.ts`,
|
||||||
|
`dist/libs/${libName}/index.js`,
|
||||||
|
`dist/libs/${libName}/index.mjs`
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
|
checkFilesExist,
|
||||||
cleanupProject,
|
cleanupProject,
|
||||||
createFile,
|
createFile,
|
||||||
exists,
|
exists,
|
||||||
@ -32,38 +33,23 @@ describe('Vite Plugin', () => {
|
|||||||
`apps/${myApp}/index.html`,
|
`apps/${myApp}/index.html`,
|
||||||
`
|
`
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang='en'>
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset='utf-8' />
|
||||||
<title>My App</title>
|
<title>My App</title>
|
||||||
<base href="/" />
|
<base href='/' />
|
||||||
|
|
||||||
<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' />
|
||||||
</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/main.tsx'></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
`
|
`
|
||||||
);
|
);
|
||||||
|
|
||||||
createFile(
|
|
||||||
`apps/${myApp}/src/environments/environment.prod.ts`,
|
|
||||||
`export const environment = {
|
|
||||||
production: true,
|
|
||||||
myTestVar: 'MyProductionValue',
|
|
||||||
};`
|
|
||||||
);
|
|
||||||
createFile(
|
|
||||||
`apps/${myApp}/src/environments/environment.ts`,
|
|
||||||
`export const environment = {
|
|
||||||
production: false,
|
|
||||||
myTestVar: 'MyDevelopmentValue',
|
|
||||||
};`
|
|
||||||
);
|
|
||||||
|
|
||||||
updateFile(
|
updateFile(
|
||||||
`apps/${myApp}/src/app/app.tsx`,
|
`apps/${myApp}/src/app/app.tsx`,
|
||||||
`
|
`
|
||||||
@ -168,20 +154,6 @@ describe('Vite Plugin', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should build application and replace files', async () => {
|
|
||||||
runCLI(`build ${myApp}`);
|
|
||||||
expect(readFile(`dist/apps/${myApp}/index.html`)).toBeDefined();
|
|
||||||
const fileArray = listFiles(`dist/apps/${myApp}/assets`);
|
|
||||||
const mainBundle = fileArray.find((file) => file.endsWith('.js'));
|
|
||||||
expect(readFile(`dist/apps/${myApp}/assets/${mainBundle}`)).toContain(
|
|
||||||
'MyProductionValue'
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
readFile(`dist/apps/${myApp}/assets/${mainBundle}`)
|
|
||||||
).not.toContain('MyDevelopmentValue');
|
|
||||||
rmDist();
|
|
||||||
}, 200000);
|
|
||||||
|
|
||||||
it('should serve application in dev mode', async () => {
|
it('should serve application in dev mode', async () => {
|
||||||
const port = 4212;
|
const port = 4212;
|
||||||
const p = await runCommandUntil(
|
const p = await runCommandUntil(
|
||||||
@ -206,76 +178,11 @@ describe('Vite Plugin', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('set up new React app with --bundler=vite option', () => {
|
|
||||||
beforeEach(() => {
|
|
||||||
proj = newProject();
|
|
||||||
runCLI(`generate @nrwl/react:app ${myApp} --bundler=vite`);
|
|
||||||
updateFile(
|
|
||||||
`apps/${myApp}/src/environments/environment.prod.ts`,
|
|
||||||
`export const environment = {
|
|
||||||
production: true,
|
|
||||||
myTestVar: 'MyProductionValue',
|
|
||||||
};`
|
|
||||||
);
|
|
||||||
updateFile(
|
|
||||||
`apps/${myApp}/src/environments/environment.ts`,
|
|
||||||
`export const environment = {
|
|
||||||
production: false,
|
|
||||||
myTestVar: 'MyDevelopmentValue',
|
|
||||||
};`
|
|
||||||
);
|
|
||||||
|
|
||||||
updateFile(
|
|
||||||
`apps/${myApp}/src/app/app.tsx`,
|
|
||||||
`
|
|
||||||
import { environment } from './../environments/environment';
|
|
||||||
export function App() {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<h1>{environment.myTestVar}</h1>
|
|
||||||
<p>Welcome ${myApp}!</p>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
export default App;
|
|
||||||
`
|
|
||||||
);
|
|
||||||
});
|
|
||||||
afterEach(() => cleanupProject());
|
|
||||||
it('should build application and replace files', async () => {
|
|
||||||
runCLI(`build ${myApp}`);
|
|
||||||
expect(readFile(`dist/apps/${myApp}/index.html`)).toBeDefined();
|
|
||||||
const fileArray = listFiles(`dist/apps/${myApp}/assets`);
|
|
||||||
const mainBundle = fileArray.find((file) => file.endsWith('.js'));
|
|
||||||
expect(readFile(`dist/apps/${myApp}/assets/${mainBundle}`)).toContain(
|
|
||||||
'MyProductionValue'
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
readFile(`dist/apps/${myApp}/assets/${mainBundle}`)
|
|
||||||
).not.toContain('MyDevelopmentValue');
|
|
||||||
rmDist();
|
|
||||||
}, 200000);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('convert React webpack app to vite using the vite:configuration generator', () => {
|
describe('convert React webpack app to vite using the vite:configuration generator', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
proj = newProject();
|
proj = newProject();
|
||||||
runCLI(`generate @nrwl/react:app ${myApp} --bundler=webpack`);
|
runCLI(`generate @nrwl/react:app ${myApp} --bundler=webpack`);
|
||||||
runCLI(`generate @nrwl/vite:configuration ${myApp}`);
|
runCLI(`generate @nrwl/vite:configuration ${myApp}`);
|
||||||
updateFile(
|
|
||||||
`apps/${myApp}/src/environments/environment.prod.ts`,
|
|
||||||
`export const environment = {
|
|
||||||
production: true,
|
|
||||||
myTestVar: 'MyProductionValue',
|
|
||||||
};`
|
|
||||||
);
|
|
||||||
updateFile(
|
|
||||||
`apps/${myApp}/src/environments/environment.ts`,
|
|
||||||
`export const environment = {
|
|
||||||
production: false,
|
|
||||||
myTestVar: 'MyDevelopmentValue',
|
|
||||||
};`
|
|
||||||
);
|
|
||||||
|
|
||||||
updateFile(
|
updateFile(
|
||||||
`apps/${myApp}/src/app/app.tsx`,
|
`apps/${myApp}/src/app/app.tsx`,
|
||||||
@ -294,18 +201,6 @@ describe('Vite Plugin', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
afterEach(() => cleanupProject());
|
afterEach(() => cleanupProject());
|
||||||
it('should build application and replace files', async () => {
|
|
||||||
runCLI(`build ${myApp}`);
|
|
||||||
expect(readFile(`dist/apps/${myApp}/index.html`)).toBeDefined();
|
|
||||||
const fileArray = listFiles(`dist/apps/${myApp}/assets`);
|
|
||||||
const mainBundle = fileArray.find((file) => file.endsWith('.js'));
|
|
||||||
expect(readFile(`dist/apps/${myApp}/assets/${mainBundle}`)).toContain(
|
|
||||||
'MyProductionValue'
|
|
||||||
);
|
|
||||||
expect(
|
|
||||||
readFile(`dist/apps/${myApp}/assets/${mainBundle}`)
|
|
||||||
).not.toContain('MyDevelopmentValue');
|
|
||||||
}, 200000);
|
|
||||||
|
|
||||||
it('should serve application in dev mode', async () => {
|
it('should serve application in dev mode', async () => {
|
||||||
const port = 4212;
|
const port = 4212;
|
||||||
@ -348,7 +243,7 @@ describe('Vite Plugin', () => {
|
|||||||
readFile(`dist/apps/${myApp}/assets/${mainBundle}`)
|
readFile(`dist/apps/${myApp}/assets/${mainBundle}`)
|
||||||
).toBeDefined();
|
).toBeDefined();
|
||||||
rmDist();
|
rmDist();
|
||||||
}, 200000);
|
}, 200_000);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('convert @nrwl/web webpack app to vite using the vite:configuration generator', () => {
|
describe('convert @nrwl/web webpack app to vite using the vite:configuration generator', () => {
|
||||||
@ -391,14 +286,16 @@ describe('Vite Plugin', () => {
|
|||||||
`Successfully ran target test for project ${myApp}`
|
`Successfully ran target test for project ${myApp}`
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
}),
|
||||||
|
100_000;
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('should be able to create libs that use vitest', () => {
|
describe('should be able to create libs that use vitest', () => {
|
||||||
const lib = uniq('my-lib');
|
const lib = uniq('my-lib');
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
proj = newProject();
|
proj = newProject();
|
||||||
});
|
}),
|
||||||
|
100_000;
|
||||||
|
|
||||||
it('should be able to run tests', async () => {
|
it('should be able to run tests', async () => {
|
||||||
runCLI(`generate @nrwl/react:lib ${lib} --unitTestRunner=vitest`);
|
runCLI(`generate @nrwl/react:lib ${lib} --unitTestRunner=vitest`);
|
||||||
@ -408,7 +305,8 @@ describe('Vite Plugin', () => {
|
|||||||
expect(result.combinedOutput).toContain(
|
expect(result.combinedOutput).toContain(
|
||||||
`Successfully ran target test for project ${lib}`
|
`Successfully ran target test for project ${lib}`
|
||||||
);
|
);
|
||||||
});
|
}),
|
||||||
|
100_000;
|
||||||
|
|
||||||
it('should be able to run tests with inSourceTests set to true', async () => {
|
it('should be able to run tests with inSourceTests set to true', async () => {
|
||||||
runCLI(
|
runCLI(
|
||||||
@ -432,6 +330,6 @@ describe('Vite Plugin', () => {
|
|||||||
|
|
||||||
const result = await runCLIAsync(`test ${lib}`);
|
const result = await runCLIAsync(`test ${lib}`);
|
||||||
expect(result.combinedOutput).toContain(`1 passed`);
|
expect(result.combinedOutput).toContain(`1 passed`);
|
||||||
});
|
}, 100_000);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -19,7 +19,6 @@ describe('app', () => {
|
|||||||
compiler: 'babel',
|
compiler: 'babel',
|
||||||
e2eTestRunner: 'cypress',
|
e2eTestRunner: 'cypress',
|
||||||
skipFormat: false,
|
skipFormat: false,
|
||||||
unitTestRunner: 'jest',
|
|
||||||
name: 'myApp',
|
name: 'myApp',
|
||||||
linter: Linter.EsLint,
|
linter: Linter.EsLint,
|
||||||
style: 'css',
|
style: 'css',
|
||||||
@ -381,6 +380,12 @@ describe('app', () => {
|
|||||||
expect(targetConfig.build.options).toEqual({
|
expect(targetConfig.build.options).toEqual({
|
||||||
outputPath: 'dist/apps/my-app',
|
outputPath: 'dist/apps/my-app',
|
||||||
});
|
});
|
||||||
|
expect(
|
||||||
|
appTree.exists(`apps/my-app/environments/environment.ts`)
|
||||||
|
).toBeFalsy();
|
||||||
|
expect(
|
||||||
|
appTree.exists(`apps/my-app/environments/environment.prod.ts`)
|
||||||
|
).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should setup the nrwl web dev server builder', async () => {
|
it('should setup the nrwl web dev server builder', async () => {
|
||||||
|
|||||||
@ -80,6 +80,7 @@ export async function applicationGenerator(host: Tree, schema: Schema) {
|
|||||||
const initTask = await reactInitGenerator(host, {
|
const initTask = await reactInitGenerator(host, {
|
||||||
...options,
|
...options,
|
||||||
skipFormat: true,
|
skipFormat: true,
|
||||||
|
skipBabelConfig: options.bundler === 'vite',
|
||||||
});
|
});
|
||||||
|
|
||||||
tasks.push(initTask);
|
tasks.push(initTask);
|
||||||
@ -88,6 +89,10 @@ export async function applicationGenerator(host: Tree, schema: Schema) {
|
|||||||
addProject(host, options);
|
addProject(host, options);
|
||||||
|
|
||||||
if (options.bundler === 'vite') {
|
if (options.bundler === 'vite') {
|
||||||
|
// We recommend users use `import.meta.env.MODE` and other variables in their code to differentiate between production and development.
|
||||||
|
// See: https://vitejs.dev/guide/env-and-mode.html
|
||||||
|
host.delete(joinPathFragments(options.appProjectRoot, 'src/environments'));
|
||||||
|
|
||||||
const viteTask = await viteConfigurationGenerator(host, {
|
const viteTask = await viteConfigurationGenerator(host, {
|
||||||
uiFramework: 'react',
|
uiFramework: 'react',
|
||||||
project: options.projectName,
|
project: options.projectName,
|
||||||
@ -111,9 +116,11 @@ export async function applicationGenerator(host: Tree, schema: Schema) {
|
|||||||
|
|
||||||
const cypressTask = await addCypress(host, options);
|
const cypressTask = await addCypress(host, options);
|
||||||
tasks.push(cypressTask);
|
tasks.push(cypressTask);
|
||||||
const jestTask = await addJest(host, options);
|
if (options.unitTestRunner === 'jest') {
|
||||||
tasks.push(jestTask);
|
const jestTask = await addJest(host, options);
|
||||||
updateSpecConfig(host, options);
|
tasks.push(jestTask);
|
||||||
|
updateSpecConfig(host, options);
|
||||||
|
}
|
||||||
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);
|
||||||
|
|||||||
@ -40,20 +40,7 @@ export function normalizeOptions(
|
|||||||
|
|
||||||
assertValidStyle(options.style);
|
assertValidStyle(options.style);
|
||||||
|
|
||||||
if (options.bundler === 'vite') {
|
const normalized = {
|
||||||
options.unitTestRunner = 'vitest';
|
|
||||||
}
|
|
||||||
|
|
||||||
options.routing = options.routing ?? false;
|
|
||||||
options.strict = options.strict ?? true;
|
|
||||||
options.classComponent = options.classComponent ?? false;
|
|
||||||
options.unitTestRunner = options.unitTestRunner ?? 'jest';
|
|
||||||
options.e2eTestRunner = options.e2eTestRunner ?? 'cypress';
|
|
||||||
options.compiler = options.compiler ?? 'babel';
|
|
||||||
options.bundler = options.bundler ?? 'webpack';
|
|
||||||
options.devServerPort ??= findFreePort(host);
|
|
||||||
|
|
||||||
return {
|
|
||||||
...options,
|
...options,
|
||||||
name: names(options.name).fileName,
|
name: names(options.name).fileName,
|
||||||
projectName: appProjectName,
|
projectName: appProjectName,
|
||||||
@ -63,5 +50,18 @@ export function normalizeOptions(
|
|||||||
fileName,
|
fileName,
|
||||||
styledModule,
|
styledModule,
|
||||||
hasStyles: options.style !== 'none',
|
hasStyles: options.style !== 'none',
|
||||||
};
|
} as NormalizedSchema;
|
||||||
|
|
||||||
|
normalized.routing = normalized.routing ?? false;
|
||||||
|
normalized.strict = normalized.strict ?? true;
|
||||||
|
normalized.classComponent = normalized.classComponent ?? false;
|
||||||
|
normalized.compiler = normalized.compiler ?? 'babel';
|
||||||
|
normalized.bundler = normalized.bundler ?? 'webpack';
|
||||||
|
normalized.unitTestRunner =
|
||||||
|
normalized.unitTestRunner ??
|
||||||
|
(normalized.bundler === 'vite' ? 'vitest' : 'jest');
|
||||||
|
normalized.e2eTestRunner = normalized.e2eTestRunner ?? 'cypress';
|
||||||
|
normalized.devServerPort ??= findFreePort(host);
|
||||||
|
|
||||||
|
return normalized;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -28,8 +28,8 @@ export function setDefaults(host: Tree, options: NormalizedSchema) {
|
|||||||
...prev,
|
...prev,
|
||||||
application: {
|
application: {
|
||||||
style: options.style,
|
style: options.style,
|
||||||
unitTestRunner: options.unitTestRunner,
|
|
||||||
linter: options.linter,
|
linter: options.linter,
|
||||||
|
bundler: options.bundler,
|
||||||
...prev.application,
|
...prev.application,
|
||||||
},
|
},
|
||||||
component: {
|
component: {
|
||||||
@ -38,7 +38,6 @@ export function setDefaults(host: Tree, options: NormalizedSchema) {
|
|||||||
},
|
},
|
||||||
library: {
|
library: {
|
||||||
style: options.style,
|
style: options.style,
|
||||||
unitTestRunner: options.unitTestRunner,
|
|
||||||
linter: options.linter,
|
linter: options.linter,
|
||||||
...prev.library,
|
...prev.library,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -7,7 +7,7 @@ export interface Schema {
|
|||||||
skipFormat: boolean;
|
skipFormat: boolean;
|
||||||
directory?: string;
|
directory?: string;
|
||||||
tags?: string;
|
tags?: string;
|
||||||
unitTestRunner: 'jest' | 'vitest' | 'none';
|
unitTestRunner?: 'jest' | 'vitest' | 'none';
|
||||||
inSourceTests?: boolean;
|
inSourceTests?: boolean;
|
||||||
/**
|
/**
|
||||||
* @deprecated
|
* @deprecated
|
||||||
@ -41,4 +41,5 @@ export interface NormalizedSchema extends Schema {
|
|||||||
fileName: string;
|
fileName: string;
|
||||||
styledModule: null | SupportedStyles;
|
styledModule: null | SupportedStyles;
|
||||||
hasStyles: boolean;
|
hasStyles: boolean;
|
||||||
|
unitTestRunner: 'jest' | 'vitest' | 'none';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -185,7 +185,7 @@
|
|||||||
"description": "The bundler to use.",
|
"description": "The bundler to use.",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["vite", "webpack"],
|
"enum": ["vite", "webpack"],
|
||||||
"x-prompt": "Which bundler do you want to use?",
|
"x-prompt": "Which bundler do you want to use to build the application?",
|
||||||
"default": "webpack"
|
"default": "webpack"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
export interface InitSchema {
|
export interface InitSchema {
|
||||||
unitTestRunner?: 'jest' | 'vitest' | 'none';
|
unitTestRunner?: 'jest' | 'vitest' | 'none';
|
||||||
e2eTestRunner?: 'cypress' | 'none';
|
e2eTestRunner?: 'cypress' | 'none';
|
||||||
|
skipBabelConfig?: boolean;
|
||||||
skipFormat?: boolean;
|
skipFormat?: boolean;
|
||||||
skipPackageJson?: boolean;
|
skipPackageJson?: boolean;
|
||||||
js?: boolean;
|
js?: boolean;
|
||||||
|
|||||||
@ -28,6 +28,11 @@
|
|||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
},
|
},
|
||||||
|
"skipBabelConfig": {
|
||||||
|
"description": "Do not generate a root babel.config.json (if babel is not needed).",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
|
},
|
||||||
"js": {
|
"js": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false,
|
"default": false,
|
||||||
|
|||||||
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "<%= name %>",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"main": "./index.js",
|
||||||
|
"module": "./index.mjs",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"import": "./index.mjs",
|
||||||
|
"require": "./index.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "<%= name %>",
|
|
||||||
"version": "0.0.1"
|
|
||||||
}
|
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title><%= className %> Demo</title>
|
||||||
|
<base href="/" />
|
||||||
|
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link rel="icon" type="image/x-icon" href="favicon.ico" />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="./src/demo.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"name": "<%= name %>",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"main": "./index.js",
|
||||||
|
"types": "./index.d.ts",
|
||||||
|
"exports": {
|
||||||
|
".": {
|
||||||
|
"import": "./index.mjs",
|
||||||
|
"require": "./index.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
/*
|
||||||
|
* This a a demo file that can be helpful when developing components by serving and interacting with them in the browser.
|
||||||
|
*/
|
||||||
|
<% if (component) { %>
|
||||||
|
import * as ReactDOM from 'react-dom/client';
|
||||||
|
import { <%= className %> } from './index';
|
||||||
|
|
||||||
|
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
|
||||||
|
root.render(
|
||||||
|
<<%= className %> />
|
||||||
|
);
|
||||||
|
<% } else { %>
|
||||||
|
import * as ReactDOM from 'react-dom/client';
|
||||||
|
|
||||||
|
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
|
||||||
|
root.render(
|
||||||
|
<h1><%= className %> Demo</h1>
|
||||||
|
);
|
||||||
|
<% } %>
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
import type { Tree } from '@nrwl/devkit';
|
||||||
|
import { Linter } from '@nrwl/linter';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
|
||||||
|
import { normalizeOptions } from './normalize-options';
|
||||||
|
|
||||||
|
describe('normalizeOptions', () => {
|
||||||
|
let tree: Tree;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tree = createTreeWithEmptyWorkspace();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set unitTestRunner=jest and bundler=rollup by default', async () => {
|
||||||
|
const options = normalizeOptions(tree, {
|
||||||
|
name: 'test',
|
||||||
|
style: 'css',
|
||||||
|
linter: Linter.None,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(options).toMatchObject({
|
||||||
|
buildable: false,
|
||||||
|
bundler: 'rollup',
|
||||||
|
compiler: 'babel',
|
||||||
|
unitTestRunner: 'jest',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set unitTestRunner=vitest by default when bundler is vite', async () => {
|
||||||
|
const options = normalizeOptions(tree, {
|
||||||
|
name: 'test',
|
||||||
|
style: 'css',
|
||||||
|
linter: Linter.None,
|
||||||
|
bundler: 'vite',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(options).toMatchObject({
|
||||||
|
buildable: true,
|
||||||
|
bundler: 'vite',
|
||||||
|
compiler: 'babel',
|
||||||
|
unitTestRunner: 'vitest',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should set maintain unitTestRunner when bundler is vite', async () => {
|
||||||
|
const options = normalizeOptions(tree, {
|
||||||
|
name: 'test',
|
||||||
|
style: 'css',
|
||||||
|
linter: Linter.None,
|
||||||
|
bundler: 'vite',
|
||||||
|
unitTestRunner: 'jest',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(options).toMatchObject({
|
||||||
|
buildable: true,
|
||||||
|
bundler: 'vite',
|
||||||
|
compiler: 'babel',
|
||||||
|
unitTestRunner: 'jest',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,79 @@
|
|||||||
|
import {
|
||||||
|
getImportPath,
|
||||||
|
getProjects,
|
||||||
|
getWorkspaceLayout,
|
||||||
|
joinPathFragments,
|
||||||
|
names,
|
||||||
|
normalizePath,
|
||||||
|
Tree,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
|
import { assertValidStyle } from '../../../utils/assertion';
|
||||||
|
import { NormalizedSchema } from '../library';
|
||||||
|
import { Schema } from '../schema';
|
||||||
|
|
||||||
|
export function normalizeOptions(
|
||||||
|
host: Tree,
|
||||||
|
options: Schema
|
||||||
|
): NormalizedSchema {
|
||||||
|
const name = names(options.name).fileName;
|
||||||
|
const projectDirectory = options.directory
|
||||||
|
? `${names(options.directory).fileName}/${name}`
|
||||||
|
: name;
|
||||||
|
|
||||||
|
const projectName = projectDirectory.replace(new RegExp('/', 'g'), '-');
|
||||||
|
const fileName = projectName;
|
||||||
|
const { libsDir, npmScope } = getWorkspaceLayout(host);
|
||||||
|
const projectRoot = joinPathFragments(libsDir, projectDirectory);
|
||||||
|
|
||||||
|
const parsedTags = options.tags
|
||||||
|
? options.tags.split(',').map((s) => s.trim())
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const importPath =
|
||||||
|
options.importPath || getImportPath(npmScope, projectDirectory);
|
||||||
|
|
||||||
|
const normalized = {
|
||||||
|
...options,
|
||||||
|
compiler: options.compiler ?? 'babel',
|
||||||
|
bundler: options.bundler ?? 'rollup',
|
||||||
|
fileName,
|
||||||
|
routePath: `/${name}`,
|
||||||
|
name: projectName,
|
||||||
|
projectRoot,
|
||||||
|
projectDirectory,
|
||||||
|
parsedTags,
|
||||||
|
importPath,
|
||||||
|
} as NormalizedSchema;
|
||||||
|
|
||||||
|
// Libraries with a bundler or is publishable must also be buildable.
|
||||||
|
normalized.buildable = Boolean(
|
||||||
|
options.bundler || options.buildable || options.publishable
|
||||||
|
);
|
||||||
|
|
||||||
|
normalized.unitTestRunner =
|
||||||
|
normalized.unitTestRunner ??
|
||||||
|
(normalized.bundler === 'vite' ? 'vitest' : 'jest');
|
||||||
|
|
||||||
|
if (options.appProject) {
|
||||||
|
const appProjectConfig = getProjects(host).get(options.appProject);
|
||||||
|
|
||||||
|
if (appProjectConfig.projectType !== 'application') {
|
||||||
|
throw new Error(
|
||||||
|
`appProject expected type of "application" but got "${appProjectConfig.projectType}"`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
normalized.appMain = appProjectConfig.targets.build.options.main;
|
||||||
|
normalized.appSourceRoot = normalizePath(appProjectConfig.sourceRoot);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(
|
||||||
|
`Could not locate project main for ${options.appProject}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertValidStyle(normalized.style);
|
||||||
|
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
@ -225,7 +225,11 @@ describe('lib', () => {
|
|||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
it('should update jest.config.ts for babel', async () => {
|
it('should update jest.config.ts for babel', async () => {
|
||||||
await libraryGenerator(appTree, { ...defaultSchema, compiler: 'babel' });
|
await libraryGenerator(appTree, {
|
||||||
|
...defaultSchema,
|
||||||
|
buildable: true,
|
||||||
|
compiler: 'babel',
|
||||||
|
});
|
||||||
expect(appTree.read('libs/my-lib/jest.config.ts', 'utf-8')).toContain(
|
expect(appTree.read('libs/my-lib/jest.config.ts', 'utf-8')).toContain(
|
||||||
"['babel-jest', { presets: ['@nrwl/react/babel'] }]"
|
"['babel-jest', { presets: ['@nrwl/react/babel'] }]"
|
||||||
);
|
);
|
||||||
@ -280,6 +284,7 @@ describe('lib', () => {
|
|||||||
await libraryGenerator(appTree, {
|
await libraryGenerator(appTree, {
|
||||||
...defaultSchema,
|
...defaultSchema,
|
||||||
directory: 'myDir',
|
directory: 'myDir',
|
||||||
|
buildable: true,
|
||||||
compiler: 'babel',
|
compiler: 'babel',
|
||||||
});
|
});
|
||||||
expect(
|
expect(
|
||||||
@ -725,6 +730,7 @@ describe('lib', () => {
|
|||||||
it('should install swc dependencies if needed', async () => {
|
it('should install swc dependencies if needed', async () => {
|
||||||
await libraryGenerator(appTree, {
|
await libraryGenerator(appTree, {
|
||||||
...defaultSchema,
|
...defaultSchema,
|
||||||
|
buildable: true,
|
||||||
compiler: 'swc',
|
compiler: 'swc',
|
||||||
});
|
});
|
||||||
const packageJson = readJson(appTree, 'package.json');
|
const packageJson = readJson(appTree, 'package.json');
|
||||||
@ -764,6 +770,7 @@ describe('lib', () => {
|
|||||||
await libraryGenerator(appTree, {
|
await libraryGenerator(appTree, {
|
||||||
...defaultSchema,
|
...defaultSchema,
|
||||||
style,
|
style,
|
||||||
|
compiler: 'babel',
|
||||||
name: 'myLib',
|
name: 'myLib',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -44,10 +44,12 @@ import {
|
|||||||
typesReactRouterDomVersion,
|
typesReactRouterDomVersion,
|
||||||
} from '../../utils/versions';
|
} from '../../utils/versions';
|
||||||
import componentGenerator from '../component/component';
|
import componentGenerator from '../component/component';
|
||||||
import init from '../init/init';
|
import initGenerator from '../init/init';
|
||||||
import { Schema } from './schema';
|
import { Schema } from './schema';
|
||||||
import { updateJestConfigContent } from '../../utils/jest-utils';
|
import { updateJestConfigContent } from '../../utils/jest-utils';
|
||||||
import { vitestGenerator } from '@nrwl/vite';
|
import { viteConfigurationGenerator, vitestGenerator } from '@nrwl/vite';
|
||||||
|
import { normalizeOptions } from './lib/normalize-options';
|
||||||
|
|
||||||
export interface NormalizedSchema extends Schema {
|
export interface NormalizedSchema extends Schema {
|
||||||
name: string;
|
name: string;
|
||||||
fileName: string;
|
fileName: string;
|
||||||
@ -57,6 +59,7 @@ export interface NormalizedSchema extends Schema {
|
|||||||
parsedTags: string[];
|
parsedTags: string[];
|
||||||
appMain?: string;
|
appMain?: string;
|
||||||
appSourceRoot?: string;
|
appSourceRoot?: string;
|
||||||
|
unitTestRunner: 'jest' | 'vitest' | 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function libraryGenerator(host: Tree, schema: Schema) {
|
export async function libraryGenerator(host: Tree, schema: Schema) {
|
||||||
@ -72,10 +75,11 @@ export async function libraryGenerator(host: Tree, schema: Schema) {
|
|||||||
options.style = 'none';
|
options.style = 'none';
|
||||||
}
|
}
|
||||||
|
|
||||||
const initTask = await init(host, {
|
const initTask = await initGenerator(host, {
|
||||||
...options,
|
...options,
|
||||||
e2eTestRunner: 'none',
|
e2eTestRunner: 'none',
|
||||||
skipFormat: true,
|
skipFormat: true,
|
||||||
|
skipBabelConfig: options.bundler === 'vite',
|
||||||
});
|
});
|
||||||
tasks.push(initTask);
|
tasks.push(initTask);
|
||||||
|
|
||||||
@ -90,6 +94,18 @@ export async function libraryGenerator(host: Tree, schema: Schema) {
|
|||||||
updateBaseTsConfig(host, options);
|
updateBaseTsConfig(host, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (options.buildable && options.bundler === 'vite') {
|
||||||
|
const viteTask = await viteConfigurationGenerator(host, {
|
||||||
|
uiFramework: 'react',
|
||||||
|
project: options.name,
|
||||||
|
newProject: true,
|
||||||
|
includeLib: true,
|
||||||
|
inSourceTests: options.inSourceTests,
|
||||||
|
includeVitest: true,
|
||||||
|
});
|
||||||
|
tasks.push(viteTask);
|
||||||
|
}
|
||||||
|
|
||||||
if (options.unitTestRunner === 'jest') {
|
if (options.unitTestRunner === 'jest') {
|
||||||
const jestTask = await jestProjectGenerator(host, {
|
const jestTask = await jestProjectGenerator(host, {
|
||||||
...options,
|
...options,
|
||||||
@ -110,7 +126,10 @@ export async function libraryGenerator(host: Tree, schema: Schema) {
|
|||||||
);
|
);
|
||||||
host.write(jestConfigPath, updatedContent);
|
host.write(jestConfigPath, updatedContent);
|
||||||
}
|
}
|
||||||
} else if (options.unitTestRunner === 'vitest') {
|
} else if (
|
||||||
|
options.unitTestRunner === 'vitest' &&
|
||||||
|
options.bundler !== 'vite' // tests are already configured if bundler is vite
|
||||||
|
) {
|
||||||
const vitestTask = await vitestGenerator(host, {
|
const vitestTask = await vitestGenerator(host, {
|
||||||
uiFramework: 'react',
|
uiFramework: 'react',
|
||||||
project: options.name,
|
project: options.name,
|
||||||
@ -299,22 +318,34 @@ function updateBaseTsConfig(host: Tree, options: NormalizedSchema) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function createFiles(host: Tree, options: NormalizedSchema) {
|
function createFiles(host: Tree, options: NormalizedSchema) {
|
||||||
|
const substitutions = {
|
||||||
|
...options,
|
||||||
|
...names(options.name),
|
||||||
|
tmpl: '',
|
||||||
|
offsetFromRoot: offsetFromRoot(options.projectRoot),
|
||||||
|
rootTsConfigPath: getRelativePathToRootTsConfig(host, options.projectRoot),
|
||||||
|
};
|
||||||
|
|
||||||
generateFiles(
|
generateFiles(
|
||||||
host,
|
host,
|
||||||
joinPathFragments(__dirname, './files/lib'),
|
joinPathFragments(__dirname, './files/common'),
|
||||||
options.projectRoot,
|
options.projectRoot,
|
||||||
{
|
substitutions
|
||||||
...options,
|
|
||||||
...names(options.name),
|
|
||||||
tmpl: '',
|
|
||||||
offsetFromRoot: offsetFromRoot(options.projectRoot),
|
|
||||||
rootTsConfigPath: getRelativePathToRootTsConfig(
|
|
||||||
host,
|
|
||||||
options.projectRoot
|
|
||||||
),
|
|
||||||
}
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (options.bundler === 'vite') {
|
||||||
|
generateFiles(
|
||||||
|
host,
|
||||||
|
joinPathFragments(__dirname, './files/vite'),
|
||||||
|
options.projectRoot,
|
||||||
|
substitutions
|
||||||
|
);
|
||||||
|
|
||||||
|
if (host.exists(joinPathFragments(options.projectRoot, '.babelrc'))) {
|
||||||
|
host.delete(joinPathFragments(options.projectRoot, '.babelrc'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!options.publishable && !options.buildable) {
|
if (!options.publishable && !options.buildable) {
|
||||||
host.delete(`${options.projectRoot}/package.json`);
|
host.delete(`${options.projectRoot}/package.json`);
|
||||||
}
|
}
|
||||||
@ -415,59 +446,6 @@ function readComponent(
|
|||||||
return { content, source };
|
return { content, source };
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeOptions(host: Tree, options: Schema): NormalizedSchema {
|
|
||||||
const name = names(options.name).fileName;
|
|
||||||
const projectDirectory = options.directory
|
|
||||||
? `${names(options.directory).fileName}/${name}`
|
|
||||||
: name;
|
|
||||||
|
|
||||||
const projectName = projectDirectory.replace(new RegExp('/', 'g'), '-');
|
|
||||||
const fileName = projectName;
|
|
||||||
const { libsDir, npmScope } = getWorkspaceLayout(host);
|
|
||||||
const projectRoot = joinPathFragments(libsDir, projectDirectory);
|
|
||||||
|
|
||||||
const parsedTags = options.tags
|
|
||||||
? options.tags.split(',').map((s) => s.trim())
|
|
||||||
: [];
|
|
||||||
|
|
||||||
const importPath =
|
|
||||||
options.importPath || getImportPath(npmScope, projectDirectory);
|
|
||||||
|
|
||||||
const normalized: NormalizedSchema = {
|
|
||||||
...options,
|
|
||||||
fileName,
|
|
||||||
routePath: `/${name}`,
|
|
||||||
name: projectName,
|
|
||||||
projectRoot,
|
|
||||||
projectDirectory,
|
|
||||||
parsedTags,
|
|
||||||
importPath,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (options.appProject) {
|
|
||||||
const appProjectConfig = getProjects(host).get(options.appProject);
|
|
||||||
|
|
||||||
if (appProjectConfig.projectType !== 'application') {
|
|
||||||
throw new Error(
|
|
||||||
`appProject expected type of "application" but got "${appProjectConfig.projectType}"`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
normalized.appMain = appProjectConfig.targets.build.options.main;
|
|
||||||
normalized.appSourceRoot = normalizePath(appProjectConfig.sourceRoot);
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error(
|
|
||||||
`Could not locate project main for ${options.appProject}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assertValidStyle(normalized.style);
|
|
||||||
|
|
||||||
return normalized;
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateLibPackageNpmScope(host: Tree, options: NormalizedSchema) {
|
function updateLibPackageNpmScope(host: Tree, options: NormalizedSchema) {
|
||||||
return updateJson(host, `${options.projectRoot}/package.json`, (json) => {
|
return updateJson(host, `${options.projectRoot}/package.json`, (json) => {
|
||||||
json.name = options.importPath;
|
json.name = options.importPath;
|
||||||
|
|||||||
@ -2,27 +2,28 @@ import { Linter } from '@nrwl/linter';
|
|||||||
import { SupportedStyles } from '../../../typings/style';
|
import { SupportedStyles } from '../../../typings/style';
|
||||||
|
|
||||||
export interface Schema {
|
export interface Schema {
|
||||||
name: string;
|
|
||||||
directory?: string;
|
|
||||||
style: SupportedStyles;
|
|
||||||
skipTsConfig: boolean;
|
|
||||||
skipFormat: boolean;
|
|
||||||
tags?: string;
|
|
||||||
pascalCaseFiles?: boolean;
|
|
||||||
routing?: boolean;
|
|
||||||
appProject?: string;
|
appProject?: string;
|
||||||
unitTestRunner: 'jest' | 'vitest' | 'none';
|
|
||||||
inSourceTests?: boolean;
|
|
||||||
linter: Linter;
|
|
||||||
component?: boolean;
|
|
||||||
publishable?: boolean;
|
|
||||||
buildable?: boolean;
|
buildable?: boolean;
|
||||||
importPath?: string;
|
bundler?: 'rollup' | 'vite';
|
||||||
js?: boolean;
|
|
||||||
globalCss?: boolean;
|
|
||||||
strict?: boolean;
|
|
||||||
setParserOptionsProject?: boolean;
|
|
||||||
standaloneConfig?: boolean;
|
|
||||||
compiler?: 'babel' | 'swc';
|
compiler?: 'babel' | 'swc';
|
||||||
|
component?: boolean;
|
||||||
|
directory?: string;
|
||||||
|
globalCss?: boolean;
|
||||||
|
importPath?: string;
|
||||||
|
inSourceTests?: boolean;
|
||||||
|
js?: boolean;
|
||||||
|
linter: Linter;
|
||||||
|
name: string;
|
||||||
|
pascalCaseFiles?: boolean;
|
||||||
|
publishable?: boolean;
|
||||||
|
routing?: boolean;
|
||||||
|
setParserOptionsProject?: boolean;
|
||||||
|
skipFormat?: boolean;
|
||||||
skipPackageJson?: boolean;
|
skipPackageJson?: boolean;
|
||||||
|
skipTsConfig?: boolean;
|
||||||
|
standaloneConfig?: boolean;
|
||||||
|
strict?: boolean;
|
||||||
|
style: SupportedStyles;
|
||||||
|
tags?: string;
|
||||||
|
unitTestRunner?: 'jest' | 'vitest' | 'none';
|
||||||
}
|
}
|
||||||
|
|||||||
@ -81,8 +81,7 @@
|
|||||||
"unitTestRunner": {
|
"unitTestRunner": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["jest", "vitest", "none"],
|
"enum": ["jest", "vitest", "none"],
|
||||||
"description": "Test runner to use for unit tests.",
|
"description": "Test runner to use for unit tests."
|
||||||
"default": "jest"
|
|
||||||
},
|
},
|
||||||
"inSourceTests": {
|
"inSourceTests": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
@ -126,7 +125,7 @@
|
|||||||
"buildable": {
|
"buildable": {
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false,
|
"default": false,
|
||||||
"description": "Generate a buildable library."
|
"description": "Generate a buildable library. If a bundler is set then the library is buildable by default."
|
||||||
},
|
},
|
||||||
"importPath": {
|
"importPath": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
@ -161,11 +160,17 @@
|
|||||||
"description": "Split the project configuration into `<projectRoot>/project.json` rather than including it inside `workspace.json`.",
|
"description": "Split the project configuration into `<projectRoot>/project.json` rather than including it inside `workspace.json`.",
|
||||||
"type": "boolean"
|
"type": "boolean"
|
||||||
},
|
},
|
||||||
|
"bundler": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The bundler to use.",
|
||||||
|
"enum": ["vite", "rollup"],
|
||||||
|
"x-prompt": "Which bundler would you like to use to build the library?"
|
||||||
|
},
|
||||||
"compiler": {
|
"compiler": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"enum": ["babel", "swc"],
|
"enum": ["babel", "swc"],
|
||||||
"default": "babel",
|
"default": "babel",
|
||||||
"description": "Which compiler to use."
|
"description": "Which compiler to use. Does not apply if bundler is set to Vite."
|
||||||
},
|
},
|
||||||
"skipPackageJson": {
|
"skipPackageJson": {
|
||||||
"description": "Do not add dependencies to `package.json`.",
|
"description": "Do not add dependencies to `package.json`.",
|
||||||
|
|||||||
@ -4,25 +4,42 @@ import 'dotenv/config';
|
|||||||
import { getBuildConfig } from '../../utils/options-utils';
|
import { getBuildConfig } from '../../utils/options-utils';
|
||||||
import { ViteBuildExecutorOptions } from './schema';
|
import { ViteBuildExecutorOptions } from './schema';
|
||||||
import { copyAssets } from '@nrwl/js';
|
import { copyAssets } from '@nrwl/js';
|
||||||
|
import { existsSync } from 'fs';
|
||||||
|
import { join } from 'path';
|
||||||
|
|
||||||
export default async function viteBuildExecutor(
|
export default async function viteBuildExecutor(
|
||||||
options: ViteBuildExecutorOptions,
|
options: ViteBuildExecutorOptions,
|
||||||
context: ExecutorContext
|
context: ExecutorContext
|
||||||
) {
|
) {
|
||||||
if (options.assets) {
|
const projectRoot = context.workspace.projects[context.projectName].root;
|
||||||
|
let assets = options.assets;
|
||||||
|
|
||||||
|
// Copy package.json as an asset if it exists
|
||||||
|
if (existsSync(join(projectRoot, 'package.json'))) {
|
||||||
|
assets ??= [];
|
||||||
|
assets.push({
|
||||||
|
input: '.',
|
||||||
|
output: '.',
|
||||||
|
glob: 'package.json',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.info(`NX Vite build starting ...`);
|
||||||
|
const buildResult = await runInstance(await getBuildConfig(options, context));
|
||||||
|
logger.info(`NX Vite build finished ...`);
|
||||||
|
logger.info(`NX Vite files available in ${options.outputPath}`);
|
||||||
|
|
||||||
|
// TODO(jack): handle watch once we add that option
|
||||||
|
if (assets) {
|
||||||
await copyAssets(
|
await copyAssets(
|
||||||
{
|
{
|
||||||
outputPath: options.outputPath,
|
outputPath: options.outputPath,
|
||||||
assets: options.assets,
|
assets: assets,
|
||||||
},
|
},
|
||||||
context
|
context
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(`NX Vite build starting ...`);
|
|
||||||
await runInstance(await getBuildConfig(options, context));
|
|
||||||
logger.info(`NX Vite build finished ...`);
|
|
||||||
logger.info(`NX Vite files available in ${options.outputPath}`);
|
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,4 +1,9 @@
|
|||||||
import { addDependenciesToPackageJson, readJson, Tree } from '@nrwl/devkit';
|
import {
|
||||||
|
addDependenciesToPackageJson,
|
||||||
|
addProjectConfiguration,
|
||||||
|
readJson,
|
||||||
|
Tree,
|
||||||
|
} from '@nrwl/devkit';
|
||||||
import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing';
|
import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing';
|
||||||
import { nxVersion } from '../../utils/versions';
|
import { nxVersion } from '../../utils/versions';
|
||||||
|
|
||||||
@ -116,4 +121,27 @@ describe('@nrwl/vite:configuration', () => {
|
|||||||
expect(viteConfig).toContain('test');
|
expect(viteConfig).toContain('test');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('library mode', () => {
|
||||||
|
beforeEach(async () => {
|
||||||
|
tree = createTreeWithEmptyV1Workspace();
|
||||||
|
addProjectConfiguration(tree, 'my-lib', {
|
||||||
|
root: 'my-lib',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should add config for building library', async () => {
|
||||||
|
await viteConfigurationGenerator(tree, {
|
||||||
|
uiFramework: 'react',
|
||||||
|
includeLib: true,
|
||||||
|
project: 'my-lib',
|
||||||
|
newProject: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const viteConfig = tree.read('my-lib/vite.config.ts').toString();
|
||||||
|
|
||||||
|
expect(viteConfig).toMatch('build: {');
|
||||||
|
expect(viteConfig).toMatch("external: ['react'");
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -35,6 +35,7 @@ export async function viteConfigurationGenerator(tree: Tree, schema: Schema) {
|
|||||||
|
|
||||||
const initTask = await initGenerator(tree, {
|
const initTask = await initGenerator(tree, {
|
||||||
uiFramework: schema.uiFramework,
|
uiFramework: schema.uiFramework,
|
||||||
|
includeLib: schema.includeLib,
|
||||||
});
|
});
|
||||||
tasks.push(initTask);
|
tasks.push(initTask);
|
||||||
|
|
||||||
|
|||||||
@ -4,4 +4,5 @@ export interface Schema {
|
|||||||
newProject?: boolean;
|
newProject?: boolean;
|
||||||
includeVitest?: boolean;
|
includeVitest?: boolean;
|
||||||
inSourceTests?: boolean;
|
inSourceTests?: boolean;
|
||||||
|
includeLib?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,12 @@
|
|||||||
"x-dropdown": "project",
|
"x-dropdown": "project",
|
||||||
"x-prompt": "What is the name of the project to set up a webpack for?"
|
"x-prompt": "What is the name of the project to set up a webpack for?"
|
||||||
},
|
},
|
||||||
|
"includeLib": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Add a library build option.",
|
||||||
|
"default": false,
|
||||||
|
"x-prompt": "Does this project contain a buildable library?"
|
||||||
|
},
|
||||||
"uiFramework": {
|
"uiFramework": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "UI Framework to use for Vite.",
|
"description": "UI Framework to use for Vite.",
|
||||||
|
|||||||
@ -5,17 +5,17 @@ import {
|
|||||||
Tree,
|
Tree,
|
||||||
updateJson,
|
updateJson,
|
||||||
} from '@nrwl/devkit';
|
} from '@nrwl/devkit';
|
||||||
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
jsdomVersion,
|
||||||
nxVersion,
|
nxVersion,
|
||||||
|
vitePluginDtsVersion,
|
||||||
vitePluginEslintVersion,
|
vitePluginEslintVersion,
|
||||||
vitePluginReactVersion,
|
vitePluginReactVersion,
|
||||||
viteVersion,
|
|
||||||
vitestUiVersion,
|
vitestUiVersion,
|
||||||
vitestVersion,
|
vitestVersion,
|
||||||
viteTsConfigPathsVersion,
|
viteTsConfigPathsVersion,
|
||||||
jsdomVersion,
|
viteVersion,
|
||||||
} from '../../utils/versions';
|
} from '../../utils/versions';
|
||||||
import { Schema } from './schema';
|
import { Schema } from './schema';
|
||||||
|
|
||||||
@ -39,6 +39,10 @@ function checkDependenciesInstalled(host: Tree, schema: Schema) {
|
|||||||
devDependencies['@vitejs/plugin-react'] = vitePluginReactVersion;
|
devDependencies['@vitejs/plugin-react'] = vitePluginReactVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (schema.includeLib) {
|
||||||
|
devDependencies['vite-plugin-dts'] = vitePluginDtsVersion;
|
||||||
|
}
|
||||||
|
|
||||||
return addDependenciesToPackageJson(host, dependencies, devDependencies);
|
return addDependenciesToPackageJson(host, dependencies, devDependencies);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
export interface Schema {
|
export interface Schema {
|
||||||
uiFramework: 'react' | 'none';
|
uiFramework: 'react' | 'none';
|
||||||
|
includeLib?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,6 +11,11 @@
|
|||||||
"enum": ["react", "none"],
|
"enum": ["react", "none"],
|
||||||
"default": "react",
|
"default": "react",
|
||||||
"x-prompt": "What UI framework plugin should Vite use?"
|
"x-prompt": "What UI framework plugin should Vite use?"
|
||||||
|
},
|
||||||
|
"includeLib": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Add dependencies needed to build libraries.",
|
||||||
|
"default": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"examplesFile": "../../../docs/init-examples.md"
|
"examplesFile": "../../../docs/init-examples.md"
|
||||||
|
|||||||
@ -107,8 +107,10 @@ describe('vitest generator', () => {
|
|||||||
import react from '@vitejs/plugin-react';
|
import react from '@vitejs/plugin-react';
|
||||||
import ViteTsConfigPathsPlugin from 'vite-tsconfig-paths';
|
import ViteTsConfigPathsPlugin from 'vite-tsconfig-paths';
|
||||||
|
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
|
|
||||||
react(),
|
react(),
|
||||||
ViteTsConfigPathsPlugin({
|
ViteTsConfigPathsPlugin({
|
||||||
root: '../../',
|
root: '../../',
|
||||||
@ -116,6 +118,7 @@ describe('vitest generator', () => {
|
|||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|
||||||
test: {
|
test: {
|
||||||
globals: true,
|
globals: true,
|
||||||
environment: 'jsdom',
|
environment: 'jsdom',
|
||||||
@ -140,14 +143,17 @@ describe('vitest generator', () => {
|
|||||||
import react from '@vitejs/plugin-react';
|
import react from '@vitejs/plugin-react';
|
||||||
import ViteTsConfigPathsPlugin from 'vite-tsconfig-paths';
|
import ViteTsConfigPathsPlugin from 'vite-tsconfig-paths';
|
||||||
|
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
|
|
||||||
react(),
|
react(),
|
||||||
ViteTsConfigPathsPlugin({
|
ViteTsConfigPathsPlugin({
|
||||||
root: '../../',
|
root: '../../',
|
||||||
projects: ['tsconfig.base.json'],
|
projects: ['tsconfig.base.json'],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|
||||||
define: {
|
define: {
|
||||||
'import.meta.vitest': undefined
|
'import.meta.vitest': undefined
|
||||||
},
|
},
|
||||||
|
|||||||
@ -150,21 +150,23 @@ export function addOrChangeBuildTarget(
|
|||||||
options: buildOptions,
|
options: buildOptions,
|
||||||
configurations: {
|
configurations: {
|
||||||
development: {},
|
development: {},
|
||||||
production: {
|
production: options.includeLib
|
||||||
fileReplacements: [
|
? {}
|
||||||
{
|
: {
|
||||||
replace: joinPathFragments(
|
fileReplacements: [
|
||||||
project.sourceRoot,
|
{
|
||||||
'environments/environment.ts'
|
replace: joinPathFragments(
|
||||||
),
|
project.sourceRoot,
|
||||||
with: joinPathFragments(
|
'environments/environment.ts'
|
||||||
project.sourceRoot,
|
),
|
||||||
'environments/environment.prod.ts'
|
with: joinPathFragments(
|
||||||
),
|
project.sourceRoot,
|
||||||
|
'environments/environment.prod.ts'
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
sourceMap: false,
|
||||||
},
|
},
|
||||||
],
|
|
||||||
sourceMap: false,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -356,7 +358,8 @@ export function writeViteConfig(tree: Tree, options: Schema) {
|
|||||||
|
|
||||||
let viteConfigContent = '';
|
let viteConfigContent = '';
|
||||||
|
|
||||||
const testOption = `test: {
|
const testOption = options.includeVitest
|
||||||
|
? `test: {
|
||||||
globals: true,
|
globals: true,
|
||||||
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}'],
|
||||||
@ -365,11 +368,45 @@ export function writeViteConfig(tree: Tree, options: Schema) {
|
|||||||
? `includeSource: ['src/**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}']`
|
? `includeSource: ['src/**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}']`
|
||||||
: ''
|
: ''
|
||||||
}
|
}
|
||||||
},`;
|
},`
|
||||||
|
: '';
|
||||||
|
|
||||||
const defineOption = `define: {
|
const defineOption = options.inSourceTests
|
||||||
|
? `define: {
|
||||||
'import.meta.vitest': undefined
|
'import.meta.vitest': undefined
|
||||||
},`;
|
},`
|
||||||
|
: '';
|
||||||
|
|
||||||
|
const dtsPlugin = `dts({
|
||||||
|
tsConfigFilePath: join(__dirname, 'tsconfig.lib.json'),
|
||||||
|
// Faster builds by skipping tests. Set this to false to enable type checking.
|
||||||
|
skipDiagnostics: true,
|
||||||
|
}),`;
|
||||||
|
|
||||||
|
const buildOption = options.includeLib
|
||||||
|
? `
|
||||||
|
// Configuration for building your library.
|
||||||
|
// See: https://vitejs.dev/guide/build.html#library-mode
|
||||||
|
build: {
|
||||||
|
lib: {
|
||||||
|
// Could also be a dictionary or array of multiple entry points.
|
||||||
|
entry: 'src/index.ts',
|
||||||
|
name: '${options.project}',
|
||||||
|
fileName: 'index',
|
||||||
|
// Change this to the formats you want to support.
|
||||||
|
// Don't forgot to update your package.json as well.
|
||||||
|
formats: ['es', 'cjs']
|
||||||
|
},
|
||||||
|
rollupOptions: {
|
||||||
|
// External packages that should not be bundled into your library.
|
||||||
|
external: [${
|
||||||
|
options.uiFramework === 'react'
|
||||||
|
? "'react', 'react-dom', 'react/jsx-runtime'"
|
||||||
|
: ''
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},`
|
||||||
|
: '';
|
||||||
|
|
||||||
switch (options.uiFramework) {
|
switch (options.uiFramework) {
|
||||||
case 'react':
|
case 'react':
|
||||||
@ -378,17 +415,20 @@ ${options.includeVitest ? '/// <reference types="vitest" />' : ''}
|
|||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
import react from '@vitejs/plugin-react';
|
import react from '@vitejs/plugin-react';
|
||||||
import ViteTsConfigPathsPlugin from 'vite-tsconfig-paths';
|
import ViteTsConfigPathsPlugin from 'vite-tsconfig-paths';
|
||||||
|
${options.includeLib ? "import dts from 'vite-plugin-dts';" : ''}
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
|
${options.includeLib ? dtsPlugin : ''}
|
||||||
react(),
|
react(),
|
||||||
ViteTsConfigPathsPlugin({
|
ViteTsConfigPathsPlugin({
|
||||||
root: '${offsetFromRoot(projectConfig.root)}',
|
root: '${offsetFromRoot(projectConfig.root)}',
|
||||||
projects: ['tsconfig.base.json'],
|
projects: ['tsconfig.base.json'],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
${options.inSourceTests ? defineOption : ''}
|
${buildOption}
|
||||||
${options.includeVitest ? testOption : ''}
|
${defineOption}
|
||||||
|
${testOption}
|
||||||
});`;
|
});`;
|
||||||
break;
|
break;
|
||||||
case 'none':
|
case 'none':
|
||||||
@ -396,16 +436,19 @@ ${options.includeVitest ? '/// <reference types="vitest" />' : ''}
|
|||||||
${options.includeVitest ? '/// <reference types="vitest" />' : ''}
|
${options.includeVitest ? '/// <reference types="vitest" />' : ''}
|
||||||
import { defineConfig } from 'vite';
|
import { defineConfig } from 'vite';
|
||||||
import ViteTsConfigPathsPlugin from 'vite-tsconfig-paths';
|
import ViteTsConfigPathsPlugin from 'vite-tsconfig-paths';
|
||||||
|
${options.includeLib ? "import dts from 'vite-plugin-dts';" : ''}
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
|
${options.includeLib ? dtsPlugin : ''}
|
||||||
ViteTsConfigPathsPlugin({
|
ViteTsConfigPathsPlugin({
|
||||||
root: '${offsetFromRoot(projectConfig.root)}',
|
root: '${offsetFromRoot(projectConfig.root)}',
|
||||||
projects: ['tsconfig.base.json'],
|
projects: ['tsconfig.base.json'],
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
${options.inSourceTests ? defineOption : ''}
|
${buildOption}
|
||||||
${options.includeVitest ? testOption : ''}
|
${defineOption}
|
||||||
|
${testOption}
|
||||||
});`;
|
});`;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
@ -8,3 +8,4 @@ export const vitePluginVueVersion = '^3.2.0';
|
|||||||
export const vitePluginVueJsxVersion = '^2.1.1';
|
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';
|
||||||
|
|||||||
@ -168,6 +168,12 @@ describe('app', () => {
|
|||||||
expect(tree.exists('apps/my-app-e2e/cypress.config.ts')).toBeTruthy();
|
expect(tree.exists('apps/my-app-e2e/cypress.config.ts')).toBeTruthy();
|
||||||
expect(tree.exists('apps/my-app/index.html')).toBeTruthy();
|
expect(tree.exists('apps/my-app/index.html')).toBeTruthy();
|
||||||
expect(tree.exists('apps/my-app/vite.config.ts')).toBeTruthy();
|
expect(tree.exists('apps/my-app/vite.config.ts')).toBeTruthy();
|
||||||
|
expect(
|
||||||
|
tree.exists(`apps/my-app/environments/environment.ts`)
|
||||||
|
).toBeFalsy();
|
||||||
|
expect(
|
||||||
|
tree.exists(`apps/my-app/environments/environment.prod.ts`)
|
||||||
|
).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should extend from root tsconfig.json when no tsconfig.base.json', async () => {
|
it('should extend from root tsconfig.json when no tsconfig.base.json', async () => {
|
||||||
|
|||||||
@ -192,6 +192,8 @@ export async function applicationGenerator(host: Tree, schema: Schema) {
|
|||||||
const webTask = await webInitGenerator(host, {
|
const webTask = await webInitGenerator(host, {
|
||||||
...options,
|
...options,
|
||||||
skipFormat: true,
|
skipFormat: true,
|
||||||
|
// Vite does not use babel by default
|
||||||
|
skipBabelConfig: options.bundler === 'vite',
|
||||||
});
|
});
|
||||||
tasks.push(webTask);
|
tasks.push(webTask);
|
||||||
|
|
||||||
@ -199,6 +201,10 @@ export async function applicationGenerator(host: Tree, schema: Schema) {
|
|||||||
await addProject(host, options);
|
await addProject(host, options);
|
||||||
|
|
||||||
if (options.bundler === 'vite') {
|
if (options.bundler === 'vite') {
|
||||||
|
// We recommend users use `import.meta.env.MODE` and other variables in their code to differentiate between production and development.
|
||||||
|
// See: https://vitejs.dev/guide/env-and-mode.html
|
||||||
|
host.delete(joinPathFragments(options.appProjectRoot, 'src/environments'));
|
||||||
|
|
||||||
const viteTask = await viteConfigurationGenerator(host, {
|
const viteTask = await viteConfigurationGenerator(host, {
|
||||||
uiFramework: 'react',
|
uiFramework: 'react',
|
||||||
project: options.projectName,
|
project: options.projectName,
|
||||||
|
|||||||
@ -42,14 +42,16 @@ function updateDependencies(tree: Tree, schema: Schema) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function initRootBabelConfig(tree: Tree) {
|
function initRootBabelConfig(tree: Tree, schema: Schema) {
|
||||||
if (tree.exists('/babel.config.json') || tree.exists('/babel.config.js')) {
|
if (tree.exists('/babel.config.json') || tree.exists('/babel.config.js')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
writeJson(tree, '/babel.config.json', {
|
if (!schema.skipBabelConfig) {
|
||||||
babelrcRoots: ['*'], // Make sure .babelrc files other than root can be loaded in a monorepo
|
writeJson(tree, '/babel.config.json', {
|
||||||
});
|
babelrcRoots: ['*'], // Make sure .babelrc files other than root can be loaded in a monorepo
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const workspaceConfiguration = readWorkspaceConfiguration(tree);
|
const workspaceConfiguration = readWorkspaceConfiguration(tree);
|
||||||
|
|
||||||
@ -80,7 +82,7 @@ export async function webInitGenerator(tree: Tree, schema: Schema) {
|
|||||||
const installTask = updateDependencies(tree, schema);
|
const installTask = updateDependencies(tree, schema);
|
||||||
tasks.push(installTask);
|
tasks.push(installTask);
|
||||||
}
|
}
|
||||||
initRootBabelConfig(tree);
|
initRootBabelConfig(tree, schema);
|
||||||
if (!schema.skipFormat) {
|
if (!schema.skipFormat) {
|
||||||
await formatFiles(tree);
|
await formatFiles(tree);
|
||||||
}
|
}
|
||||||
|
|||||||
1
packages/web/src/generators/init/schema.d.ts
vendored
1
packages/web/src/generators/init/schema.d.ts
vendored
@ -4,4 +4,5 @@ export interface Schema {
|
|||||||
e2eTestRunner?: 'cypress' | 'none';
|
e2eTestRunner?: 'cypress' | 'none';
|
||||||
skipFormat?: boolean;
|
skipFormat?: boolean;
|
||||||
skipPackageJson?: boolean;
|
skipPackageJson?: boolean;
|
||||||
|
skipBabelConfig?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -33,6 +33,11 @@
|
|||||||
"description": "Do not add dependencies to `package.json`.",
|
"description": "Do not add dependencies to `package.json`.",
|
||||||
"type": "boolean",
|
"type": "boolean",
|
||||||
"default": false
|
"default": false
|
||||||
|
},
|
||||||
|
"skipBabelConfig": {
|
||||||
|
"description": "Do not generate a root babel.config.json (if babel is not needed).",
|
||||||
|
"type": "boolean",
|
||||||
|
"default": false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": []
|
"required": []
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user