feat(bundling): vite generators (#13158)
This commit is contained in:
parent
91f4635e0c
commit
f394608658
@ -234,9 +234,16 @@
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"hidden": true
|
||||
},
|
||||
"bundler": {
|
||||
"description": "The bundler to use.",
|
||||
"enum": ["vite", "webpack"],
|
||||
"x-prompt": "Which bundler do you want to use?",
|
||||
"default": "webpack"
|
||||
}
|
||||
},
|
||||
"required": [],
|
||||
"examplesFile": "## Examples\n\n{% tabs %}\n{% tab label=\"Simple Application\" %}\n\nCreate an application named `my-app`:\n\n```bash\nnx g @nrwl/react:application my-app\n```\n\n{% /tab %}\n\n{% tab label=\"Application using Vite as bundler\" %}\n\nCreate an application named `my-app`:\n\n```bash\nnx g @nrwl/react:app my-app --bundler=vite\n```\n\n{% /tab %}\n\n{% tab label=\"Specify directory and style extension\" %}\n\nCreate an application named `my-app` in the `my-dir` directory and use `scss` for styles:\n\n```bash\nnx g @nrwl/react:app my-app --directory=my-dir --style=scss\n```\n\n{% /tab %}\n\n{% tab label=\"Add tags\" %}\n\nAdd tags to the application (used for linting).\n\n```bash\nnx g @nrwl/react:app my-app --tags=scope:admin,type:ui\n```\n\n{% /tab %}\n{% /tabs %}\n",
|
||||
"presets": []
|
||||
},
|
||||
"aliases": ["app"],
|
||||
@ -1056,6 +1063,12 @@
|
||||
"devServerPort": {
|
||||
"type": "number",
|
||||
"description": "The port for the dev server of the remote app."
|
||||
},
|
||||
"bundler": {
|
||||
"description": "The bundler to use.",
|
||||
"enum": ["vite", "webpack"],
|
||||
"x-prompt": "Which bundler do you want to use?",
|
||||
"default": "webpack"
|
||||
}
|
||||
},
|
||||
"required": ["name"],
|
||||
@ -1217,6 +1230,12 @@
|
||||
"devServerPort": {
|
||||
"type": "number",
|
||||
"description": "The port for the dev server of the remote app."
|
||||
},
|
||||
"bundler": {
|
||||
"description": "The bundler to use.",
|
||||
"enum": ["vite", "webpack"],
|
||||
"x-prompt": "Which bundler do you want to use?",
|
||||
"default": "webpack"
|
||||
}
|
||||
},
|
||||
"required": ["name"],
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -29,7 +29,7 @@
|
||||
"bundler": {
|
||||
"type": "string",
|
||||
"description": "The bundler to use.",
|
||||
"enum": ["webpack", "none"],
|
||||
"enum": ["webpack", "none", "vite"],
|
||||
"default": "webpack"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
@ -119,8 +119,9 @@
|
||||
"bundler": {
|
||||
"type": "string",
|
||||
"description": "The bundler to use.",
|
||||
"enum": ["webpack", "none"],
|
||||
"default": "webpack"
|
||||
"enum": ["webpack", "none", "vite"],
|
||||
"default": "webpack",
|
||||
"x-prompt": "Which bundler do you want to use?"
|
||||
},
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
@ -160,6 +161,7 @@
|
||||
}
|
||||
},
|
||||
"required": [],
|
||||
"examplesFile": "## Examples\n\n{% tabs %}\n{% tab label=\"Simple Application\" %}\n\nCreate an application named `my-app`:\n\n```bash\nnx g @nrwl/web:application my-app\n```\n\n{% /tab %}\n\n{% tab label=\"Application using Vite as bundler\" %}\n\nCreate an application named `my-app`:\n\n```bash\nnx g @nrwl/web:app my-app --bundler=vite\n```\n\n{% /tab %}\n\n{% tab label=\"Specify directory\" %}\n\nCreate an application named `my-app` in the `my-dir` directory:\n\n```bash\nnx g @nrwl/web:app my-app --directory=my-dir\n```\n\n{% /tab %}\n\n{% tab label=\"Add tags\" %}\n\nAdd tags to the application (used for linting).\n\n```bash\nnx g @nrwl/web:app my-app --tags=scope:admin,type:ui\n```\n\n{% /tab %}\n{% /tabs %}\n",
|
||||
"presets": []
|
||||
},
|
||||
"aliases": ["app"],
|
||||
|
||||
@ -371,7 +371,7 @@
|
||||
"path": "generated/packages/vite.json",
|
||||
"schemas": {
|
||||
"executors": ["dev-server", "build", "test"],
|
||||
"generators": ["init"]
|
||||
"generators": ["init", "configuration"]
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@ -59,7 +59,29 @@ nx g @nrwl/vite:init
|
||||
You will notice that the executor will ask you of the framework you are planning to use. This is just to make sure that the right dependencies are installed. You can always install manually any other dependencies you need.
|
||||
{% /callout %}
|
||||
|
||||
## Using Vite.js in your applications
|
||||
## Generate an application using Vite
|
||||
|
||||
You can generate a React or a Web application that uses Vite.js. The `@nrwl/react:app` and `@nrwl/web:app` generators accept the `bundler` option, where you can pass `vite`. This will generate a new application configured to use Vite.js, and it will also install all the necessary dependencies.
|
||||
|
||||
To generate a React application using Vite.js, run the following:
|
||||
|
||||
```bash
|
||||
nx g @nrwl/react:app my-app --bundler=vite
|
||||
```
|
||||
|
||||
To generate a Web application using Vite.js, run the following:
|
||||
|
||||
```bash
|
||||
nx g @nrwl/web:app my-app --bundler=vite
|
||||
```
|
||||
|
||||
## Modify an existing React or Web application to use Vite.js
|
||||
|
||||
You can use the `@nrwl/vite:configuration` generator to change your React or Web application to use Vite.js. This generator will modify your application's configuration to use Vite.js, and it will also install all the necessary dependencies.
|
||||
|
||||
You can read more about this generator on the [`@nrwl/vite:configuration`](/packages/vite/generators/configuration) generator page.
|
||||
|
||||
## Set up your apps to use Vite.js manually
|
||||
|
||||
You can use the `@nrwl/vite:dev-server` and the `@nrwl/vite:build` executors to serve and build your applications using Vite.js. To do this, you need to make a few adjustments to your application.
|
||||
|
||||
|
||||
@ -16,7 +16,9 @@ describe('React Cypress Component Tests', () => {
|
||||
|
||||
beforeAll(() => {
|
||||
projectName = newProject({ name: uniq('cy-react') });
|
||||
runCLI(`generate @nrwl/react:app ${appName} --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nrwl/react:app ${appName} --bundler=webpack --no-interactive`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nrwl/react:component fancy-cmp --project=${appName} --no-interactive`
|
||||
);
|
||||
|
||||
@ -25,7 +25,9 @@ describe('React Module Federation', () => {
|
||||
const remote2 = uniq('remote2');
|
||||
const remote3 = uniq('remote3');
|
||||
|
||||
runCLI(`generate @nrwl/react:host ${shell} --style=css --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nrwl/react:host ${shell} --bundler=webpack --style=css --no-interactive`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nrwl/react:remote ${remote1} --style=css --host=${shell} --no-interactive`
|
||||
);
|
||||
|
||||
@ -28,7 +28,9 @@ describe('React Applications', () => {
|
||||
const libName = uniq('lib');
|
||||
const libWithNoComponents = uniq('lib');
|
||||
|
||||
runCLI(`generate @nrwl/react:app ${appName} --style=css --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nrwl/react:app ${appName} --style=css --bundler=webpack --no-interactive`
|
||||
);
|
||||
runCLI(`generate @nrwl/react:lib ${libName} --style=css --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nrwl/react:lib ${libWithNoComponents} --no-interactive --no-component`
|
||||
@ -63,7 +65,9 @@ describe('React Applications', () => {
|
||||
it('should generate app with legacy-ie support', async () => {
|
||||
const appName = uniq('app');
|
||||
|
||||
runCLI(`generate @nrwl/react:app ${appName} --style=css --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nrwl/react:app ${appName} --style=css --bundler=webpack --no-interactive`
|
||||
);
|
||||
|
||||
// changing browser support of this application
|
||||
updateFile(`apps/${appName}/.browserslistrc`, `IE 11`);
|
||||
@ -90,7 +94,9 @@ describe('React Applications', () => {
|
||||
const appName = uniq('app');
|
||||
const libName = uniq('lib');
|
||||
|
||||
runCLI(`generate @nrwl/react:app ${appName} --no-interactive --js`);
|
||||
runCLI(
|
||||
`generate @nrwl/react:app ${appName} --bundler=webpack --no-interactive --js`
|
||||
);
|
||||
runCLI(`generate @nrwl/react:lib ${libName} --no-interactive --js`);
|
||||
|
||||
const mainPath = `apps/${appName}/src/main.js`;
|
||||
@ -173,7 +179,7 @@ describe('React Applications: --style option', () => {
|
||||
`('should support global and css modules', ({ style }) => {
|
||||
const appName = uniq('app');
|
||||
runCLI(
|
||||
`generate @nrwl/react:app ${appName} --style=${style} --no-interactive`
|
||||
`generate @nrwl/react:app ${appName} --style=${style} --bundler=webpack --no-interactive`
|
||||
);
|
||||
|
||||
// make sure stylePreprocessorOptions works
|
||||
@ -210,7 +216,9 @@ describe('React Applications: additional packages', () => {
|
||||
it('should generate app with routing', async () => {
|
||||
const appName = uniq('app');
|
||||
|
||||
runCLI(`generate @nrwl/react:app ${appName} --routing --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nrwl/react:app ${appName} --routing --bundler=webpack --no-interactive`
|
||||
);
|
||||
|
||||
runCLI(`build ${appName} --outputHashing none`);
|
||||
|
||||
@ -226,7 +234,7 @@ describe('React Applications: additional packages', () => {
|
||||
const appName = uniq('app');
|
||||
const libName = uniq('lib');
|
||||
|
||||
runCLI(`g @nrwl/react:app ${appName} --no-interactive`);
|
||||
runCLI(`g @nrwl/react:app ${appName} --bundler=webpack --no-interactive`);
|
||||
runCLI(`g @nrwl/react:redux lemon --project=${appName}`);
|
||||
runCLI(`g @nrwl/react:lib ${libName} --no-interactive`);
|
||||
runCLI(`g @nrwl/react:redux orange --project=${libName}`);
|
||||
@ -252,7 +260,7 @@ describe('React Applications and Libs with PostCSS', () => {
|
||||
const appName = uniq('app');
|
||||
const libName = uniq('lib');
|
||||
|
||||
runCLI(`g @nrwl/react:app ${appName} --no-interactive`);
|
||||
runCLI(`g @nrwl/react:app ${appName} --bundler=webpack --no-interactive`);
|
||||
runCLI(`g @nrwl/react:lib ${libName} --no-interactive`);
|
||||
|
||||
const mainPath = `apps/${appName}/src/main.tsx`;
|
||||
|
||||
@ -19,13 +19,14 @@ const myApp = uniq('my-app');
|
||||
describe('Vite Plugin', () => {
|
||||
let proj: string;
|
||||
|
||||
beforeEach(() => {
|
||||
proj = newProject();
|
||||
runCLI(`generate @nrwl/react:app ${myApp}`);
|
||||
runCLI(`generate @nrwl/vite:init`);
|
||||
updateFile(
|
||||
`apps/${myApp}/index.html`,
|
||||
`
|
||||
describe('set up new project manually', () => {
|
||||
beforeEach(() => {
|
||||
proj = newProject();
|
||||
runCLI(`generate @nrwl/react:app ${myApp}`);
|
||||
runCLI(`generate @nrwl/vite:init`);
|
||||
updateFile(
|
||||
`apps/${myApp}/index.html`,
|
||||
`
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
@ -38,15 +39,15 @@ describe('Vite Plugin', () => {
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="src/main.tsx"></script>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
);
|
||||
);
|
||||
|
||||
createFile(
|
||||
`apps/${myApp}/vite.config.ts`,
|
||||
`
|
||||
createFile(
|
||||
`apps/${myApp}/vite.config.ts`,
|
||||
`
|
||||
/// <reference types="vitest" />
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
@ -65,11 +66,11 @@ describe('Vite Plugin', () => {
|
||||
environment: 'jsdom',
|
||||
}
|
||||
});`
|
||||
);
|
||||
);
|
||||
|
||||
updateFile(
|
||||
`apps/${myApp}/tsconfig.json`,
|
||||
`
|
||||
updateFile(
|
||||
`apps/${myApp}/tsconfig.json`,
|
||||
`
|
||||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
@ -102,56 +103,130 @@ describe('Vite Plugin', () => {
|
||||
]
|
||||
}
|
||||
`
|
||||
);
|
||||
);
|
||||
|
||||
updateProjectConfig(myApp, (config) => {
|
||||
config.targets.build.executor = '@nrwl/vite:build';
|
||||
config.targets.serve.executor = '@nrwl/vite:dev-server';
|
||||
config.targets.test.executor = '@nrwl/vite:test';
|
||||
updateProjectConfig(myApp, (config) => {
|
||||
config.targets.build.executor = '@nrwl/vite:build';
|
||||
config.targets.serve.executor = '@nrwl/vite:dev-server';
|
||||
config.targets.test.executor = '@nrwl/vite:test';
|
||||
|
||||
config.targets.build.options = {
|
||||
outputPath: `dist/apps/${myApp}`,
|
||||
};
|
||||
config.targets.build.options = {
|
||||
outputPath: `dist/apps/${myApp}`,
|
||||
};
|
||||
|
||||
config.targets.serve.options = {
|
||||
buildTarget: `${myApp}:build`,
|
||||
};
|
||||
config.targets.serve.options = {
|
||||
buildTarget: `${myApp}:build`,
|
||||
};
|
||||
|
||||
config.targets.serve.options = {
|
||||
config: `apps/${myApp}/vite.config.ts`,
|
||||
};
|
||||
config.targets.serve.options = {
|
||||
config: `apps/${myApp}/vite.config.ts`,
|
||||
};
|
||||
|
||||
return config;
|
||||
return config;
|
||||
});
|
||||
});
|
||||
afterEach(() => cleanupProject());
|
||||
|
||||
it('should build applications', async () => {
|
||||
runCLI(`build ${myApp}`);
|
||||
expect(readFile(`dist/apps/${myApp}/index.html`)).toBeDefined();
|
||||
rmDist();
|
||||
}, 200000);
|
||||
|
||||
it('should serve applications in dev mode', async () => {
|
||||
const port = 4212;
|
||||
const p = await runCommandUntil(
|
||||
`run ${myApp}:serve --port=${port}`,
|
||||
(output) => {
|
||||
return output.includes('Local:');
|
||||
}
|
||||
);
|
||||
try {
|
||||
await promisifiedTreeKill(p.pid, 'SIGKILL');
|
||||
await killPorts(port);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}, 200000);
|
||||
|
||||
it('should test applications', async () => {
|
||||
const result = await runCLIAsync(`test ${myApp}`);
|
||||
expect(result.combinedOutput).toContain(
|
||||
`Successfully ran target test for project ${myApp}`
|
||||
);
|
||||
});
|
||||
});
|
||||
afterEach(() => cleanupProject());
|
||||
|
||||
it('should build applications', async () => {
|
||||
runCLI(`build ${myApp}`);
|
||||
expect(readFile(`dist/apps/${myApp}/index.html`)).toBeDefined();
|
||||
rmDist();
|
||||
}, 200000);
|
||||
describe('set up new React project with --bundler=vite option', () => {
|
||||
beforeEach(() => {
|
||||
proj = newProject();
|
||||
runCLI(`generate @nrwl/react:app ${myApp} --bundler=vite`);
|
||||
});
|
||||
afterEach(() => cleanupProject());
|
||||
it('should build applications', async () => {
|
||||
runCLI(`build ${myApp}`);
|
||||
expect(readFile(`dist/apps/${myApp}/index.html`)).toBeDefined();
|
||||
rmDist();
|
||||
}, 200000);
|
||||
|
||||
it('should serve applications in dev mode', async () => {
|
||||
const port = 4212;
|
||||
const p = await runCommandUntil(
|
||||
`run ${myApp}:serve --port=${port}`,
|
||||
(output) => {
|
||||
return output.includes('Local:');
|
||||
it('should serve applications in dev mode', async () => {
|
||||
const port = 4212;
|
||||
const p = await runCommandUntil(
|
||||
`run ${myApp}:serve --port=${port}`,
|
||||
(output) => {
|
||||
return output.includes('Local:');
|
||||
}
|
||||
);
|
||||
try {
|
||||
await promisifiedTreeKill(p.pid, 'SIGKILL');
|
||||
await killPorts(port);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
);
|
||||
try {
|
||||
await promisifiedTreeKill(p.pid, 'SIGKILL');
|
||||
await killPorts(port);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}, 200000);
|
||||
}, 200000);
|
||||
|
||||
it('should test applications', async () => {
|
||||
const result = await runCLIAsync(`test ${myApp}`);
|
||||
expect(result.combinedOutput).toContain(
|
||||
`Successfully ran target test for project ${myApp}`
|
||||
);
|
||||
it('should test applications', async () => {
|
||||
const result = await runCLIAsync(`test ${myApp}`);
|
||||
expect(result.combinedOutput).toContain(
|
||||
`Successfully ran target test for project ${myApp}`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('convert React webpack project to vite using the vite:configuration generator', () => {
|
||||
beforeEach(() => {
|
||||
proj = newProject();
|
||||
runCLI(`generate @nrwl/react:app ${myApp} --bundler=webpack`);
|
||||
runCLI(`generate @nrwl/vite:configuration ${myApp}`);
|
||||
});
|
||||
afterEach(() => cleanupProject());
|
||||
it('should build applications', async () => {
|
||||
runCLI(`build ${myApp}`);
|
||||
expect(readFile(`dist/apps/${myApp}/index.html`)).toBeDefined();
|
||||
rmDist();
|
||||
}, 200000);
|
||||
|
||||
it('should serve applications in dev mode', async () => {
|
||||
const port = 4212;
|
||||
const p = await runCommandUntil(
|
||||
`run ${myApp}:serve --port=${port}`,
|
||||
(output) => {
|
||||
return output.includes('Local:');
|
||||
}
|
||||
);
|
||||
try {
|
||||
await promisifiedTreeKill(p.pid, 'SIGKILL');
|
||||
await killPorts(port);
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
}, 200000);
|
||||
|
||||
it('should test applications', async () => {
|
||||
const result = await runCLIAsync(`test ${myApp}`);
|
||||
expect(result.combinedOutput).toContain(
|
||||
`Successfully ran target test for project ${myApp}`
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
94
e2e/web/src/web-vite.test.ts
Normal file
94
e2e/web/src/web-vite.test.ts
Normal file
@ -0,0 +1,94 @@
|
||||
import {
|
||||
checkFilesDoNotExist,
|
||||
checkFilesExist,
|
||||
cleanupProject,
|
||||
createFile,
|
||||
isNotWindows,
|
||||
killPorts,
|
||||
newProject,
|
||||
removeFile,
|
||||
runCLI,
|
||||
runCLIAsync,
|
||||
runCypressTests,
|
||||
uniq,
|
||||
} from '@nrwl/e2e/utils';
|
||||
|
||||
describe('Web Components Applications with bundler set as vite', () => {
|
||||
beforeEach(() => newProject());
|
||||
afterEach(() => cleanupProject());
|
||||
|
||||
it('should be able to generate a web app', async () => {
|
||||
const appName = uniq('app');
|
||||
runCLI(`generate @nrwl/web:app ${appName} --bundler=vite --no-interactive`);
|
||||
|
||||
const lintResults = runCLI(`lint ${appName}`);
|
||||
expect(lintResults).toContain('All files pass linting.');
|
||||
|
||||
runCLI(`build ${appName}`);
|
||||
checkFilesExist(`dist/apps/${appName}/index.html`);
|
||||
|
||||
const testResults = await runCLIAsync(`test ${appName}`);
|
||||
|
||||
expect(testResults.combinedOutput).toContain(
|
||||
'Test Suites: 1 passed, 1 total'
|
||||
);
|
||||
|
||||
const lintE2eResults = runCLI(`lint ${appName}-e2e`);
|
||||
|
||||
expect(lintE2eResults).toContain('All files pass linting.');
|
||||
|
||||
if (isNotWindows() && runCypressTests()) {
|
||||
const e2eResults = runCLI(`e2e ${appName}-e2e --no-watch`);
|
||||
expect(e2eResults).toContain('All specs passed!');
|
||||
expect(await killPorts()).toBeTruthy();
|
||||
}
|
||||
}, 500000);
|
||||
|
||||
it('should be able to generate a web app with standaloneConfig', async () => {
|
||||
const appName = uniq('app');
|
||||
runCLI(
|
||||
`generate @nrwl/web:app ${appName} --bundler=vite --no-interactive --standalone-config`
|
||||
);
|
||||
|
||||
checkFilesExist(`apps/${appName}/project.json`);
|
||||
|
||||
const lintResults = runCLI(`lint ${appName}`);
|
||||
expect(lintResults).toContain('All files pass linting.');
|
||||
}, 120000);
|
||||
|
||||
it('should remove previous output before building', async () => {
|
||||
const appName = uniq('app');
|
||||
const libName = uniq('lib');
|
||||
|
||||
runCLI(`generate @nrwl/web:app ${appName} --bundler=vite --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nrwl/react:lib ${libName} --buildable --no-interactive --compiler swc`
|
||||
);
|
||||
|
||||
createFile(`dist/apps/${appName}/_should_remove.txt`);
|
||||
createFile(`dist/libs/${libName}/_should_remove.txt`);
|
||||
createFile(`dist/apps/_should_not_remove.txt`);
|
||||
checkFilesExist(
|
||||
`dist/apps/${appName}/_should_remove.txt`,
|
||||
`dist/apps/_should_not_remove.txt`
|
||||
);
|
||||
runCLI(`build ${appName}`);
|
||||
runCLI(`build ${libName}`);
|
||||
checkFilesDoNotExist(
|
||||
`dist/apps/${appName}/_should_remove.txt`,
|
||||
`dist/libs/${libName}/_should_remove.txt`
|
||||
);
|
||||
checkFilesExist(`dist/apps/_should_not_remove.txt`);
|
||||
}, 120000);
|
||||
|
||||
it('should support workspaces w/o workspace config file', async () => {
|
||||
removeFile('workspace.json');
|
||||
const myapp = uniq('myapp');
|
||||
runCLI(`generate @nrwl/web:app ${myapp} --bundler=vite --directory=myDir`);
|
||||
|
||||
runCLI(`build my-dir-${myapp}`);
|
||||
expect(() =>
|
||||
checkFilesDoNotExist('workspace.json', 'angular.json')
|
||||
).not.toThrow();
|
||||
}, 1000000);
|
||||
});
|
||||
@ -23,7 +23,9 @@ describe('Web Components Applications', () => {
|
||||
|
||||
it('should be able to generate a web app', async () => {
|
||||
const appName = uniq('app');
|
||||
runCLI(`generate @nrwl/web:app ${appName} --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nrwl/web:app ${appName} --bundler=webpack --no-interactive`
|
||||
);
|
||||
|
||||
const lintResults = runCLI(`lint ${appName}`);
|
||||
expect(lintResults).toContain('All files pass linting.');
|
||||
@ -61,7 +63,7 @@ describe('Web Components Applications', () => {
|
||||
it('should be able to generate a web app with standaloneConfig', async () => {
|
||||
const appName = uniq('app');
|
||||
runCLI(
|
||||
`generate @nrwl/web:app ${appName} --no-interactive --standalone-config`
|
||||
`generate @nrwl/web:app ${appName} --bundler=webpack --no-interactive --standalone-config`
|
||||
);
|
||||
|
||||
checkFilesExist(`apps/${appName}/project.json`);
|
||||
@ -74,7 +76,9 @@ describe('Web Components Applications', () => {
|
||||
const appName = uniq('app');
|
||||
const libName = uniq('lib');
|
||||
|
||||
runCLI(`generate @nrwl/web:app ${appName} --no-interactive --compiler swc`);
|
||||
runCLI(
|
||||
`generate @nrwl/web:app ${appName} --bundler=webpack --no-interactive --compiler swc`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nrwl/react:lib ${libName} --buildable --no-interactive --compiler swc`
|
||||
);
|
||||
@ -112,7 +116,9 @@ describe('Web Components Applications', () => {
|
||||
it('should do another build if differential loading is needed', async () => {
|
||||
const appName = uniq('app');
|
||||
|
||||
runCLI(`generate @nrwl/web:app ${appName} --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nrwl/web:app ${appName} --bundler=webpack --no-interactive`
|
||||
);
|
||||
|
||||
updateFile(`apps/${appName}/browserslist`, `IE 9-11`);
|
||||
|
||||
@ -126,7 +132,9 @@ describe('Web Components Applications', () => {
|
||||
|
||||
it('should emit decorator metadata when it is enabled in tsconfig', async () => {
|
||||
const appName = uniq('app');
|
||||
runCLI(`generate @nrwl/web:app ${appName} --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nrwl/web:app ${appName} --bundler=webpack --no-interactive`
|
||||
);
|
||||
|
||||
updateFile(`apps/${appName}/src/app/app.element.ts`, (content) => {
|
||||
const newContent = `${content}
|
||||
@ -180,7 +188,9 @@ describe('Web Components Applications', () => {
|
||||
it('should support workspaces w/o workspace config file', async () => {
|
||||
removeFile('workspace.json');
|
||||
const myapp = uniq('myapp');
|
||||
runCLI(`generate @nrwl/web:app ${myapp} --directory=myDir`);
|
||||
runCLI(
|
||||
`generate @nrwl/web:app ${myapp} --bundler=webpack --directory=myDir`
|
||||
);
|
||||
|
||||
runCLI(`build my-dir-${myapp}`);
|
||||
expect(() =>
|
||||
@ -190,7 +200,9 @@ describe('Web Components Applications', () => {
|
||||
|
||||
it('should support custom webpackConfig option', async () => {
|
||||
const appName = uniq('app');
|
||||
runCLI(`generate @nrwl/web:app ${appName} --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nrwl/web:app ${appName} --bundler=webpack --no-interactive`
|
||||
);
|
||||
|
||||
updateProjectConfig(appName, (config) => {
|
||||
config.targets.build.options.webpackConfig = `apps/${appName}/webpack.config.js`;
|
||||
@ -284,7 +296,9 @@ describe('CLI - Environment Variables', () => {
|
||||
const nxSharedEnv = process.env.NX_SHARED_ENV;
|
||||
`;
|
||||
|
||||
runCLI(`generate @nrwl/web:app ${appName} --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nrwl/web:app ${appName} --bundler=webpack --no-interactive`
|
||||
);
|
||||
|
||||
const content = readFile(main);
|
||||
|
||||
@ -307,7 +321,9 @@ describe('CLI - Environment Variables', () => {
|
||||
const main2 = `apps/${appName2}/src/main.ts`;
|
||||
const newCode2 = `const envVars = [process.env.NODE_ENV, process.env.NX_BUILD, process.env.NX_API, process.env.NX_WS_BASE, process.env.NX_WS_ENV_LOCAL, process.env.NX_WS_LOCAL_ENV, process.env.NX_APP_BASE, process.env.NX_APP_ENV_LOCAL, process.env.NX_APP_LOCAL_ENV, process.env.NX_SHARED_ENV];`;
|
||||
|
||||
runCLI(`generate @nrwl/web:app ${appName2} --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nrwl/web:app ${appName2} --bundler=webpack --no-interactive`
|
||||
);
|
||||
|
||||
const content2 = readFile(main2);
|
||||
|
||||
@ -336,7 +352,9 @@ describe('Build Options', () => {
|
||||
|
||||
const appName = uniq('app');
|
||||
|
||||
runCLI(`generate @nrwl/web:app ${appName} --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nrwl/web:app ${appName} --bundler=webpack --no-interactive`
|
||||
);
|
||||
|
||||
const srcPath = `apps/${appName}/src`;
|
||||
const fooCss = `${srcPath}/foo.css`;
|
||||
@ -413,7 +431,9 @@ describe('index.html interpolation', () => {
|
||||
test('should interpolate environment variables', () => {
|
||||
const appName = uniq('app');
|
||||
|
||||
runCLI(`generate @nrwl/web:app ${appName} --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nrwl/web:app ${appName} --bundler=webpack --no-interactive`
|
||||
);
|
||||
|
||||
const srcPath = `apps/${appName}/src`;
|
||||
const indexPath = `${srcPath}/index.html`;
|
||||
|
||||
43
packages/react/docs/application-examples.md
Normal file
43
packages/react/docs/application-examples.md
Normal file
@ -0,0 +1,43 @@
|
||||
## Examples
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="Simple Application" %}
|
||||
|
||||
Create an application named `my-app`:
|
||||
|
||||
```bash
|
||||
nx g @nrwl/react:application my-app
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Application using Vite as bundler" %}
|
||||
|
||||
Create an application named `my-app`:
|
||||
|
||||
```bash
|
||||
nx g @nrwl/react:app my-app --bundler=vite
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Specify directory and style extension" %}
|
||||
|
||||
Create an application named `my-app` in the `my-dir` directory and use `scss` for styles:
|
||||
|
||||
```bash
|
||||
nx g @nrwl/react:app my-app --directory=my-dir --style=scss
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Add tags" %}
|
||||
|
||||
Add tags to the application (used for linting).
|
||||
|
||||
```bash
|
||||
nx g @nrwl/react:app my-app --tags=scope:admin,type:ui
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
@ -39,6 +39,7 @@
|
||||
"@nrwl/js": "file:../js",
|
||||
"@nrwl/linter": "file:../linter",
|
||||
"@nrwl/storybook": "file:../storybook",
|
||||
"@nrwl/vite": "file:../vite",
|
||||
"@nrwl/web": "file:../web",
|
||||
"@nrwl/webpack": "file:../webpack",
|
||||
"@nrwl/workspace": "file:../workspace",
|
||||
|
||||
@ -328,7 +328,11 @@ describe('app', () => {
|
||||
});
|
||||
|
||||
it('should setup the nrwl web build builder', async () => {
|
||||
await applicationGenerator(appTree, { ...schema, name: 'my-app' });
|
||||
await applicationGenerator(appTree, {
|
||||
...schema,
|
||||
name: 'my-app',
|
||||
bundler: 'webpack',
|
||||
});
|
||||
|
||||
const workspaceJson = getProjects(appTree);
|
||||
const targetConfig = workspaceJson.get('my-app').targets;
|
||||
@ -363,8 +367,28 @@ describe('app', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should setup the nrwl vite builder if bundler is vite', async () => {
|
||||
await applicationGenerator(appTree, {
|
||||
...schema,
|
||||
name: 'my-app',
|
||||
bundler: 'vite',
|
||||
});
|
||||
|
||||
const workspaceJson = getProjects(appTree);
|
||||
const targetConfig = workspaceJson.get('my-app').targets;
|
||||
expect(targetConfig.build.executor).toEqual('@nrwl/vite:build');
|
||||
expect(targetConfig.build.outputs).toEqual(['{options.outputPath}']);
|
||||
expect(targetConfig.build.options).toEqual({
|
||||
outputPath: 'dist/apps/my-app',
|
||||
});
|
||||
});
|
||||
|
||||
it('should setup the nrwl web dev server builder', async () => {
|
||||
await applicationGenerator(appTree, { ...schema, name: 'my-app' });
|
||||
await applicationGenerator(appTree, {
|
||||
...schema,
|
||||
name: 'my-app',
|
||||
bundler: 'webpack',
|
||||
});
|
||||
|
||||
const workspaceJson = getProjects(appTree);
|
||||
const targetConfig = workspaceJson.get('my-app').targets;
|
||||
@ -379,6 +403,25 @@ describe('app', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should setup the nrwl vite dev server builder if bundler is vite', async () => {
|
||||
await applicationGenerator(appTree, {
|
||||
...schema,
|
||||
name: 'my-app',
|
||||
bundler: 'vite',
|
||||
});
|
||||
|
||||
const workspaceJson = getProjects(appTree);
|
||||
const targetConfig = workspaceJson.get('my-app').targets;
|
||||
expect(targetConfig.serve.executor).toEqual('@nrwl/vite:dev-server');
|
||||
expect(targetConfig.serve.options).toEqual({
|
||||
buildTarget: 'my-app:build',
|
||||
});
|
||||
expect(targetConfig.serve.configurations.production).toEqual({
|
||||
buildTarget: 'my-app:build:production',
|
||||
hmr: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should setup the eslint builder', async () => {
|
||||
await applicationGenerator(appTree, { ...schema, name: 'my-app' });
|
||||
|
||||
@ -584,7 +627,11 @@ describe('app', () => {
|
||||
});
|
||||
|
||||
it('should exclude styles from workspace.json', async () => {
|
||||
await applicationGenerator(appTree, { ...schema, style: 'none' });
|
||||
await applicationGenerator(appTree, {
|
||||
...schema,
|
||||
style: 'none',
|
||||
bundler: 'webpack',
|
||||
});
|
||||
|
||||
const workspaceJson = getProjects(appTree);
|
||||
|
||||
@ -592,6 +639,20 @@ describe('app', () => {
|
||||
[]
|
||||
);
|
||||
});
|
||||
|
||||
it('should not break if bundler is vite', async () => {
|
||||
await applicationGenerator(appTree, {
|
||||
...schema,
|
||||
style: 'none',
|
||||
bundler: 'vite',
|
||||
});
|
||||
|
||||
const workspaceJson = getProjects(appTree);
|
||||
|
||||
expect(
|
||||
workspaceJson.get('my-app').targets.build.options.styles
|
||||
).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('--style styled-components', () => {
|
||||
@ -658,6 +719,7 @@ describe('app', () => {
|
||||
await applicationGenerator(appTree, {
|
||||
...schema,
|
||||
style: '@emotion/styled',
|
||||
bundler: 'webpack',
|
||||
});
|
||||
|
||||
const workspaceJson = getProjects(appTree);
|
||||
@ -667,6 +729,20 @@ describe('app', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should not break if bundler is vite', async () => {
|
||||
await applicationGenerator(appTree, {
|
||||
...schema,
|
||||
style: '@emotion/styled',
|
||||
bundler: 'vite',
|
||||
});
|
||||
|
||||
const workspaceJson = getProjects(appTree);
|
||||
|
||||
expect(
|
||||
workspaceJson.get('my-app').targets.build.options.styles
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should add dependencies to package.json', async () => {
|
||||
await applicationGenerator(appTree, {
|
||||
...schema,
|
||||
@ -735,9 +811,10 @@ describe('app', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should adds custom webpack config', async () => {
|
||||
it('should add custom webpack config', async () => {
|
||||
await applicationGenerator(appTree, {
|
||||
...schema,
|
||||
bundler: 'webpack',
|
||||
});
|
||||
|
||||
const workspaceJson = getProjects(appTree);
|
||||
@ -747,6 +824,19 @@ describe('app', () => {
|
||||
).toEqual('@nrwl/react/plugins/webpack');
|
||||
});
|
||||
|
||||
it('should NOT add custom webpack config if bundler is vite', async () => {
|
||||
await applicationGenerator(appTree, {
|
||||
...schema,
|
||||
bundler: 'vite',
|
||||
});
|
||||
|
||||
const workspaceJson = getProjects(appTree);
|
||||
|
||||
expect(
|
||||
workspaceJson.get('my-app').targets.build.options.webpackConfig
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should add required polyfills for core-js and regenerator', async () => {
|
||||
await applicationGenerator(appTree, {
|
||||
...schema,
|
||||
@ -839,6 +929,7 @@ describe('app', () => {
|
||||
await applicationGenerator(appTree, {
|
||||
...schema,
|
||||
rootProject: true,
|
||||
bundler: 'webpack',
|
||||
});
|
||||
expect(appTree.read('/src/main.tsx')).toBeDefined();
|
||||
expect(appTree.read('/e2e/cypress.config.ts')).toBeDefined();
|
||||
@ -851,5 +942,60 @@ describe('app', () => {
|
||||
].options['outputPath']
|
||||
).toEqual('dist/my-app');
|
||||
});
|
||||
|
||||
it('should create files at the root if bundler is vite', async () => {
|
||||
await applicationGenerator(appTree, {
|
||||
...schema,
|
||||
name: 'my-app2',
|
||||
rootProject: true,
|
||||
bundler: 'vite',
|
||||
});
|
||||
expect(appTree.read('/src/main.tsx')).toBeDefined();
|
||||
expect(appTree.read('/e2e/cypress.config.ts')).toBeDefined();
|
||||
expect(readJson(appTree, '/tsconfig.json').extends).toEqual(
|
||||
'./tsconfig.base.json'
|
||||
);
|
||||
expect(
|
||||
readJson(appTree, '/workspace.json').projects['my-app2'].architect[
|
||||
'build'
|
||||
].options['outputPath']
|
||||
).toEqual('dist/my-app2');
|
||||
});
|
||||
});
|
||||
|
||||
describe('setup React app with --bundler=vite', () => {
|
||||
let viteAppTree: Tree;
|
||||
beforeAll(async () => {
|
||||
viteAppTree = createTreeWithEmptyV1Workspace();
|
||||
await applicationGenerator(viteAppTree, { ...schema, bundler: 'vite' });
|
||||
});
|
||||
|
||||
it('should setup targets with vite configuration', () => {
|
||||
const workspaceJson = getProjects(viteAppTree);
|
||||
const targetConfig = workspaceJson.get('my-app').targets;
|
||||
expect(targetConfig.build.executor).toEqual('@nrwl/vite:build');
|
||||
expect(targetConfig.serve.executor).toEqual('@nrwl/vite:dev-server');
|
||||
expect(targetConfig.serve.options).toEqual({
|
||||
buildTarget: 'my-app:build',
|
||||
});
|
||||
});
|
||||
it('should add dependencies in package.json', () => {
|
||||
const packageJson = readJson(viteAppTree, '/package.json');
|
||||
|
||||
expect(packageJson.devDependencies).toMatchObject({
|
||||
vite: expect.any(String),
|
||||
'@vitejs/plugin-react': expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('should create correct tsconfig compilerOptions', () => {
|
||||
const tsconfigJson = readJson(viteAppTree, '/apps/my-app/tsconfig.json');
|
||||
expect(tsconfigJson.compilerOptions.types).toMatchObject(['vite/client']);
|
||||
});
|
||||
|
||||
it('should create index.html and vite.config file at the root of the app', () => {
|
||||
expect(viteAppTree.exists('/apps/my-app/index.html')).toBe(true);
|
||||
expect(viteAppTree.exists('/apps/my-app/vite.config.ts')).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -26,6 +26,7 @@ import reactInitGenerator from '../init/init';
|
||||
import { Linter, lintProjectGenerator } from '@nrwl/linter';
|
||||
import { swcCoreVersion } from '@nrwl/js/src/utils/versions';
|
||||
import { swcLoaderVersion } from '@nrwl/webpack/src/utils/versions';
|
||||
import { viteConfigurationGenerator } from '@nrwl/vite';
|
||||
|
||||
async function addLinting(host: Tree, options: NormalizedSchema) {
|
||||
const tasks: GeneratorCallback[] = [];
|
||||
@ -69,6 +70,8 @@ async function addLinting(host: Tree, options: NormalizedSchema) {
|
||||
}
|
||||
|
||||
export async function applicationGenerator(host: Tree, schema: Schema) {
|
||||
const tasks = [];
|
||||
|
||||
const options = normalizeOptions(host, schema);
|
||||
|
||||
const initTask = await reactInitGenerator(host, {
|
||||
@ -76,28 +79,39 @@ export async function applicationGenerator(host: Tree, schema: Schema) {
|
||||
skipFormat: true,
|
||||
});
|
||||
|
||||
tasks.push(initTask);
|
||||
|
||||
createApplicationFiles(host, options);
|
||||
addProject(host, options);
|
||||
|
||||
if (options.bundler === 'vite') {
|
||||
const viteTask = await viteConfigurationGenerator(host, {
|
||||
uiFramework: 'react',
|
||||
project: options.projectName,
|
||||
newProject: true,
|
||||
});
|
||||
tasks.push(viteTask);
|
||||
}
|
||||
|
||||
const lintTask = await addLinting(host, options);
|
||||
tasks.push(lintTask);
|
||||
|
||||
const cypressTask = await addCypress(host, options);
|
||||
tasks.push(cypressTask);
|
||||
const jestTask = await addJest(host, options);
|
||||
tasks.push(jestTask);
|
||||
updateJestConfig(host, options);
|
||||
const styledTask = addStyledModuleDependencies(host, options.styledModule);
|
||||
tasks.push(styledTask);
|
||||
const routingTask = addRouting(host, options);
|
||||
tasks.push(routingTask);
|
||||
setDefaults(host, options);
|
||||
|
||||
if (!options.skipFormat) {
|
||||
await formatFiles(host);
|
||||
}
|
||||
|
||||
return runTasksInSerial(
|
||||
initTask,
|
||||
lintTask,
|
||||
cypressTask,
|
||||
jestTask,
|
||||
styledTask,
|
||||
routingTask
|
||||
);
|
||||
return runTasksInSerial(...tasks);
|
||||
}
|
||||
|
||||
export default applicationGenerator;
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title><%= className %></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/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,26 @@
|
||||
import { render } from '@testing-library/react';
|
||||
<% if (routing) { %>
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
<% } %>
|
||||
|
||||
import App from './<%= fileName %>';
|
||||
|
||||
describe('App', () => {
|
||||
it('should render successfully', () => {
|
||||
<% if (routing) { %>
|
||||
const { baseElement } = render(<BrowserRouter><App /></BrowserRouter>);
|
||||
<% } else { %>
|
||||
const { baseElement } = render(<App />);
|
||||
<% } %>
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should have a greeting as the title', () => {
|
||||
<% if (routing) { %>
|
||||
const { getByText } = render(<BrowserRouter><App /></BrowserRouter>);
|
||||
<% } else { %>
|
||||
const { getByText } = render(<App />);
|
||||
<% } %>
|
||||
expect(getByText(/Welcome <%= projectName %>/gi)).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,820 @@
|
||||
/*
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
This is a starter component and can be deleted.
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
Delete this file and get started with your project!
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
*/
|
||||
export function NxWelcome({ title }: { title: string }) {
|
||||
return (
|
||||
<>
|
||||
<style
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `
|
||||
html {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
||||
'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif,
|
||||
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
|
||||
'Noto Color Emoji';
|
||||
line-height: 1.5;
|
||||
tab-size: 4;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
body {
|
||||
font-family: inherit;
|
||||
line-height: inherit;
|
||||
margin: 0;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
p,
|
||||
pre {
|
||||
margin: 0;
|
||||
}
|
||||
*,
|
||||
::before,
|
||||
::after {
|
||||
box-sizing: border-box;
|
||||
border-width: 0;
|
||||
border-style: solid;
|
||||
border-color: currentColor;
|
||||
}
|
||||
h1,
|
||||
h2 {
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
pre {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
||||
'Liberation Mono', 'Courier New', monospace;
|
||||
}
|
||||
svg {
|
||||
display: block;
|
||||
vertical-align: middle;
|
||||
shape-rendering: auto;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
pre {
|
||||
background-color: rgba(55, 65, 81, 1);
|
||||
border-radius: 0.25rem;
|
||||
color: rgba(229, 231, 235, 1);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
||||
'Liberation Mono', 'Courier New', monospace;
|
||||
overflow: scroll;
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
|
||||
.shadow {
|
||||
box-shadow: 0 0 #0000, 0 0 #0000, 0 10px 15px -3px rgba(0, 0, 0, 0.1),
|
||||
0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.rounded {
|
||||
border-radius: 1.5rem;
|
||||
}
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
.container {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 768px;
|
||||
padding-bottom: 3rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
color: rgba(55, 65, 81, 1);
|
||||
width: 100%;
|
||||
}
|
||||
#welcome {
|
||||
margin-top: 2.5rem;
|
||||
}
|
||||
#welcome h1 {
|
||||
font-size: 3rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: -0.025em;
|
||||
line-height: 1;
|
||||
}
|
||||
#welcome span {
|
||||
display: block;
|
||||
font-size: 1.875rem;
|
||||
font-weight: 300;
|
||||
line-height: 2.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
#hero {
|
||||
align-items: center;
|
||||
background-color: hsla(214, 62%, 21%, 1);
|
||||
border: none;
|
||||
box-sizing: border-box;
|
||||
color: rgba(55, 65, 81, 1);
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
margin-top: 3.5rem;
|
||||
}
|
||||
#hero .text-container {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
padding: 3rem 2rem;
|
||||
}
|
||||
#hero .text-container h2 {
|
||||
font-size: 1.5rem;
|
||||
line-height: 2rem;
|
||||
position: relative;
|
||||
}
|
||||
#hero .text-container h2 svg {
|
||||
color: hsla(162, 47%, 50%, 1);
|
||||
height: 2rem;
|
||||
left: -0.25rem;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 2rem;
|
||||
}
|
||||
#hero .text-container h2 span {
|
||||
margin-left: 2.5rem;
|
||||
}
|
||||
#hero .text-container a {
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
border-radius: 0.75rem;
|
||||
color: rgba(55, 65, 81, 1);
|
||||
display: inline-block;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 2rem;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
#hero .logo-container {
|
||||
display: none;
|
||||
justify-content: center;
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
}
|
||||
#hero .logo-container svg {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
width: 66.666667%;
|
||||
}
|
||||
#middle-content {
|
||||
align-items: flex-start;
|
||||
display: grid;
|
||||
gap: 4rem;
|
||||
grid-template-columns: 1fr;
|
||||
margin-top: 3.5rem;
|
||||
}
|
||||
#learning-materials {
|
||||
padding: 2.5rem 2rem;
|
||||
}
|
||||
#learning-materials h2 {
|
||||
font-weight: 500;
|
||||
font-size: 1.25rem;
|
||||
letter-spacing: -0.025em;
|
||||
line-height: 1.75rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
.list-item-link {
|
||||
align-items: center;
|
||||
border-radius: 0.75rem;
|
||||
display: flex;
|
||||
margin-top: 1rem;
|
||||
padding: 1rem;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
width: 100%;
|
||||
}
|
||||
.list-item-link svg:first-child {
|
||||
margin-right: 1rem;
|
||||
height: 1.5rem;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
width: 1.5rem;
|
||||
}
|
||||
.list-item-link > span {
|
||||
flex-grow: 1;
|
||||
font-weight: 400;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
.list-item-link > span > span {
|
||||
color: rgba(107, 114, 128, 1);
|
||||
display: block;
|
||||
flex-grow: 1;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 300;
|
||||
line-height: 1rem;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
.list-item-link svg:last-child {
|
||||
height: 1rem;
|
||||
transition-property: all;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
width: 1rem;
|
||||
}
|
||||
.list-item-link:hover {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
background-color: hsla(162, 47%, 50%, 1);
|
||||
}
|
||||
.list-item-link:hover > span {}
|
||||
.list-item-link:hover > span > span {
|
||||
color: rgba(243, 244, 246, 1);
|
||||
}
|
||||
.list-item-link:hover svg:last-child {
|
||||
transform: translateX(0.25rem);
|
||||
}
|
||||
#other-links {}
|
||||
.button-pill {
|
||||
padding: 1.5rem 2rem;
|
||||
transition-duration: 300ms;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
.button-pill svg {
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
flex-shrink: 0;
|
||||
width: 3rem;
|
||||
}
|
||||
.button-pill > span {
|
||||
letter-spacing: -0.025em;
|
||||
font-weight: 400;
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.75rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
.button-pill span span {
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 300;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
.button-pill:hover svg,
|
||||
.button-pill:hover {
|
||||
color: rgba(255, 255, 255, 1) !important;
|
||||
}
|
||||
#nx-console:hover {
|
||||
background-color: rgba(0, 122, 204, 1);
|
||||
}
|
||||
#nx-console svg {
|
||||
color: rgba(0, 122, 204, 1);
|
||||
}
|
||||
#nx-repo:hover {
|
||||
background-color: rgba(24, 23, 23, 1);
|
||||
}
|
||||
#nx-repo svg {
|
||||
color: rgba(24, 23, 23, 1);
|
||||
}
|
||||
#nx-cloud {
|
||||
margin-bottom: 2rem;
|
||||
margin-top: 2rem;
|
||||
padding: 2.5rem 2rem;
|
||||
}
|
||||
#nx-cloud > div {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
#nx-cloud > div svg {
|
||||
border-radius: 0.375rem;
|
||||
flex-shrink: 0;
|
||||
width: 3rem;
|
||||
}
|
||||
#nx-cloud > div h2 {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 400;
|
||||
letter-spacing: -0.025em;
|
||||
line-height: 1.75rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
#nx-cloud > div h2 span {
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 300;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
#nx-cloud p {
|
||||
font-size: 1rem;
|
||||
line-height: 1.5rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
#nx-cloud pre {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
#nx-cloud a {
|
||||
color: rgba(107, 114, 128, 1);
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
margin-top: 1.5rem;
|
||||
text-align: right;
|
||||
}
|
||||
#nx-cloud a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
#commands {
|
||||
padding: 2.5rem 2rem;
|
||||
margin-top: 3.5rem;
|
||||
}
|
||||
#commands h2 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 400;
|
||||
letter-spacing: -0.025em;
|
||||
line-height: 1.75rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
#commands p {
|
||||
font-size: 1rem;
|
||||
font-weight: 300;
|
||||
line-height: 1.5rem;
|
||||
margin-top: 1rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
details {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin-top: 1rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
details pre > span {
|
||||
color: rgba(181, 181, 181, 1);
|
||||
display: block;
|
||||
}
|
||||
summary {
|
||||
border-radius: 0.5rem;
|
||||
display: flex;
|
||||
font-weight: 400;
|
||||
padding: 0.5rem;
|
||||
cursor: pointer;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
summary:hover {
|
||||
background-color: rgba(243, 244, 246, 1);
|
||||
}
|
||||
summary svg {
|
||||
height: 1.5rem;
|
||||
margin-right: 1rem;
|
||||
width: 1.5rem;
|
||||
}
|
||||
#love {
|
||||
color: rgba(107, 114, 128, 1);
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
margin-top: 3.5rem;
|
||||
opacity: 0.6;
|
||||
text-align: center;
|
||||
}
|
||||
#love svg {
|
||||
color: rgba(252, 165, 165, 1);
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
display: inline;
|
||||
margin-top: -0.25rem;
|
||||
}
|
||||
@media screen and (min-width: 768px) {
|
||||
#hero {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
#hero .logo-container {
|
||||
display: flex;
|
||||
}
|
||||
#middle-content {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
`,
|
||||
}}
|
||||
/>
|
||||
<div className="wrapper">
|
||||
<div className="container">
|
||||
<div id="welcome">
|
||||
<h1>
|
||||
<span> Hello there, </span>
|
||||
Welcome {title} 👋
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<div id="hero" className="rounded">
|
||||
<div className="text-container">
|
||||
<h2>
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z"
|
||||
/>
|
||||
</svg>
|
||||
<span>You're up and running</span>
|
||||
</h2>
|
||||
<a href="#commands"> What's next? </a>
|
||||
</div>
|
||||
<div className="logo-container">
|
||||
<svg
|
||||
fill="currentColor"
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M11.987 14.138l-3.132 4.923-5.193-8.427-.012 8.822H0V4.544h3.691l5.247 8.833.005-3.998 3.044 4.759zm.601-5.761c.024-.048 0-3.784.008-3.833h-3.65c.002.059-.005 3.776-.003 3.833h3.645zm5.634 4.134a2.061 2.061 0 0 0-1.969 1.336 1.963 1.963 0 0 1 2.343-.739c.396.161.917.422 1.33.283a2.1 2.1 0 0 0-1.704-.88zm3.39 1.061c-.375-.13-.8-.277-1.109-.681-.06-.08-.116-.17-.176-.265a2.143 2.143 0 0 0-.533-.642c-.294-.216-.68-.322-1.18-.322a2.482 2.482 0 0 0-2.294 1.536 2.325 2.325 0 0 1 4.002.388.75.75 0 0 0 .836.334c.493-.105.46.36 1.203.518v-.133c-.003-.446-.246-.55-.75-.733zm2.024 1.266a.723.723 0 0 0 .347-.638c-.01-2.957-2.41-5.487-5.37-5.487a5.364 5.364 0 0 0-4.487 2.418c-.01-.026-1.522-2.39-1.538-2.418H8.943l3.463 5.423-3.379 5.32h3.54l1.54-2.366 1.568 2.366h3.541l-3.21-5.052a.7.7 0 0 1-.084-.32 2.69 2.69 0 0 1 2.69-2.691h.001c1.488 0 1.736.89 2.057 1.308.634.826 1.9.464 1.9 1.541a.707.707 0 0 0 1.066.596zm.35.133c-.173.372-.56.338-.755.639-.176.271.114.412.114.412s.337.156.538-.311c.104-.231.14-.488.103-.74z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="middle-content">
|
||||
<div id="learning-materials" className="rounded shadow">
|
||||
<h2>Learning materials</h2>
|
||||
<a
|
||||
href="https://nx.dev/getting-started/intro?utm_source=nx-project"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="list-item-link"
|
||||
>
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
|
||||
/>
|
||||
</svg>
|
||||
<span>
|
||||
Documentation
|
||||
<span> Everything is in there </span>
|
||||
</span>
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M9 5l7 7-7 7"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
<a
|
||||
href="https://blog.nrwl.io/?utm_source=nx-project"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="list-item-link"
|
||||
>
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"
|
||||
/>
|
||||
</svg>
|
||||
<span>
|
||||
Blog
|
||||
<span> Changelog, features & events </span>
|
||||
</span>
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M9 5l7 7-7 7"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
<a
|
||||
href="https://www.youtube.com/c/Nrwl_io/videos?utm_source=nx-project&sub_confirmation=1"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="list-item-link"
|
||||
>
|
||||
<svg
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>YouTube</title>
|
||||
<path d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z" />
|
||||
</svg>
|
||||
<span>
|
||||
YouTube channel
|
||||
<span> Nx Show, talks & tutorials </span>
|
||||
</span>
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M9 5l7 7-7 7"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
<a
|
||||
href="https://nx.dev/react-tutorial/1-code-generation?utm_source=nx-project"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="list-item-link"
|
||||
>
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M15 15l-2 5L9 9l11 4-5 2zm0 0l5 5M7.188 2.239l.777 2.897M5.136 7.965l-2.898-.777M13.95 4.05l-2.122 2.122m-5.657 5.656l-2.12 2.122"
|
||||
/>
|
||||
</svg>
|
||||
<span>
|
||||
Interactive tutorials
|
||||
<span> Create an app, step-by-step </span>
|
||||
</span>
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M9 5l7 7-7 7"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
<a
|
||||
href="https://nxplaybook.com/?utm_source=nx-project"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="list-item-link"
|
||||
>
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M12 14l9-5-9-5-9 5 9 5z" />
|
||||
<path d="M12 14l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14z" />
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M12 14l9-5-9-5-9 5 9 5zm0 0l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14zm-4 6v-7.5l4-2.222"
|
||||
/>
|
||||
</svg>
|
||||
<span>
|
||||
Video courses
|
||||
<span> Nx custom courses </span>
|
||||
</span>
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M9 5l7 7-7 7"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
<div id="other-links">
|
||||
<a
|
||||
id="nx-console"
|
||||
className="button-pill rounded shadow"
|
||||
href="https://marketplace.visualstudio.com/items?itemName=nrwl.angular-console&utm_source=nx-project"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>Visual Studio Code</title>
|
||||
<path d="M23.15 2.587L18.21.21a1.494 1.494 0 0 0-1.705.29l-9.46 8.63-4.12-3.128a.999.999 0 0 0-1.276.057L.327 7.261A1 1 0 0 0 .326 8.74L3.899 12 .326 15.26a1 1 0 0 0 .001 1.479L1.65 17.94a.999.999 0 0 0 1.276.057l4.12-3.128 9.46 8.63a1.492 1.492 0 0 0 1.704.29l4.942-2.377A1.5 1.5 0 0 0 24 20.06V3.939a1.5 1.5 0 0 0-.85-1.352zm-5.146 14.861L10.826 12l7.178-5.448v10.896z" />
|
||||
</svg>
|
||||
<span>
|
||||
Install Nx Console
|
||||
<span>Plugin for VSCode</span>
|
||||
</span>
|
||||
</a>
|
||||
<div id="nx-cloud" className="rounded shadow">
|
||||
<div>
|
||||
<svg
|
||||
viewBox="0 0 120 120"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M120 15V30C103.44 30 90 43.44 90 60C90 76.56 76.56 90 60 90C43.44 90 30 103.44 30 120H15C6.72 120 0 113.28 0 105V15C0 6.72 6.72 0 15 0H105C113.28 0 120 6.72 120 15Z"
|
||||
fill="#0E2039"
|
||||
/>
|
||||
<path
|
||||
d="M120 30V105C120 113.28 113.28 120 105 120H30C30 103.44 43.44 90 60 90C76.56 90 90 76.56 90 60C90 43.44 103.44 30 120 30Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
<h2>
|
||||
NxCloud
|
||||
<span>Enable faster CI & better DX</span>
|
||||
</h2>
|
||||
</div>
|
||||
<p>
|
||||
You can activate distributed tasks executions and caching by
|
||||
running:
|
||||
</p>
|
||||
<pre>nx connect-to-nx-cloud</pre>
|
||||
<a
|
||||
href="https://nx.app/?utm_source=nx-project"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
{' '}
|
||||
What is Nx Cloud?{' '}
|
||||
</a>
|
||||
</div>
|
||||
<a
|
||||
id="nx-repo"
|
||||
className="button-pill rounded shadow"
|
||||
href="https://github.com/nrwl/nx?utm_source=nx-project"
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
|
||||
</svg>
|
||||
<span>
|
||||
Nx is open source
|
||||
<span> Love Nx? Give us a star! </span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="commands" className="rounded shadow">
|
||||
<h2>Next steps</h2>
|
||||
<p>Here are some things you can do with Nx:</p>
|
||||
<details>
|
||||
<summary>
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
Add UI library
|
||||
</summary>
|
||||
<pre>
|
||||
<span># Generate UI lib</span>
|
||||
nx g @nrwl/react:lib ui
|
||||
<span># Add a component</span>
|
||||
nx g @nrwl/react:component button --project ui
|
||||
</pre>
|
||||
</details>
|
||||
<details>
|
||||
<summary>
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
View interactive project graph
|
||||
</summary>
|
||||
<pre>nx graph</pre>
|
||||
</details>
|
||||
<details>
|
||||
<summary>
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
Run affected commands
|
||||
</summary>
|
||||
<pre>
|
||||
<span># see what's been affected by changes</span>
|
||||
nx affected:graph
|
||||
<span># run tests for current changes</span>
|
||||
nx affected:test
|
||||
<span># run e2e tests for current changes</span>
|
||||
nx affected:e2e
|
||||
</pre>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<p id="love">
|
||||
Carefully crafted with
|
||||
<svg
|
||||
fill="currentColor"
|
||||
stroke="none"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth="2"
|
||||
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
|
||||
/>
|
||||
</svg>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default NxWelcome;
|
||||
@ -0,0 +1,3 @@
|
||||
export const environment = {
|
||||
production: true
|
||||
};
|
||||
@ -0,0 +1,6 @@
|
||||
// This file can be replaced during build by using the `fileReplacements` array.
|
||||
// When building for production, this file is replaced with `environment.prod.ts`.
|
||||
|
||||
export const environment = {
|
||||
production: false
|
||||
};
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@ -0,0 +1,10 @@
|
||||
<% if (strict) { %>import { StrictMode } from 'react';<% } %>
|
||||
import * as ReactDOM from 'react-dom/client';
|
||||
<% if (routing) { %>import { BrowserRouter } from 'react-router-dom';<% } %>
|
||||
|
||||
import App from './app/<%= fileName %>';
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement);
|
||||
root.render(
|
||||
<% if (strict) { %><StrictMode><% } %><% if (routing) { %><BrowserRouter><% } %><App /><% if (routing) { %></BrowserRouter><% } %><% if (strict) { %></StrictMode><% } %>
|
||||
);
|
||||
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Polyfill stable language features. These imports will be optimized by `@babel/preset-env`.
|
||||
*
|
||||
* See: https://github.com/zloirock/core-js#babel
|
||||
*/
|
||||
import 'core-js/stable';
|
||||
import 'regenerator-runtime/runtime';
|
||||
@ -0,0 +1,14 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "<%= offsetFromRoot %>dist/out-tsc",
|
||||
"types": ["node"]
|
||||
},
|
||||
"files": [
|
||||
<% if (style === 'styled-jsx') { %>"<%= offsetFromRoot %>node_modules/@nrwl/react/typings/styled-jsx.d.ts",<% } %>
|
||||
"<%= offsetFromRoot %>node_modules/@nrwl/react/typings/cssmodule.d.ts",
|
||||
"<%= offsetFromRoot %>node_modules/@nrwl/react/typings/image.d.ts"
|
||||
],
|
||||
"exclude": ["jest.config.ts","**/*.spec.ts", "**/*.test.ts", "**/*.spec.tsx", "**/*.test.tsx", "**/*.spec.js", "**/*.test.js", "**/*.spec.jsx", "**/*.test.jsx"],
|
||||
"include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"]
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
{
|
||||
"extends": "<%= rootTsConfigPath %>",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
<% if (style === '@emotion/styled') { %>"jsxImportSource": "@emotion/react",<% } %>
|
||||
"allowJs": false,
|
||||
"esModuleInterop": false,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"isolatedModules": true,
|
||||
"lib": ["DOM", "DOM.Iterable", "ESNext"],
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "Node",
|
||||
"noEmit": true,
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"target": "ESNext",
|
||||
"types": ["vite/client"],
|
||||
"useDefineForClassFields": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -11,13 +11,17 @@ export function addProject(host, options: NormalizedSchema) {
|
||||
root: options.appProjectRoot,
|
||||
sourceRoot: `${options.appProjectRoot}/src`,
|
||||
projectType: 'application',
|
||||
targets: {
|
||||
build: createBuildTarget(options),
|
||||
serve: createServeTarget(options),
|
||||
},
|
||||
targets: {},
|
||||
tags: options.parsedTags,
|
||||
};
|
||||
|
||||
if (options.bundler === 'webpack') {
|
||||
project.targets = {
|
||||
build: createBuildTarget(options),
|
||||
serve: createServeTarget(options),
|
||||
};
|
||||
}
|
||||
|
||||
addProjectConfiguration(
|
||||
host,
|
||||
options.projectName,
|
||||
|
||||
@ -60,7 +60,10 @@ export function createApplicationFiles(host: Tree, options: NormalizedSchema) {
|
||||
|
||||
generateFiles(
|
||||
host,
|
||||
join(__dirname, '../files/common'),
|
||||
join(
|
||||
__dirname,
|
||||
options.bundler === 'vite' ? '../files/common-vite' : '../files/common'
|
||||
),
|
||||
options.appProjectRoot,
|
||||
templateVariables
|
||||
);
|
||||
|
||||
@ -29,6 +29,7 @@ export interface Schema {
|
||||
skipDefaultProject?: boolean;
|
||||
skipPackageJson?: boolean;
|
||||
rootProject?: boolean;
|
||||
bundler?: 'webpack' | 'vite';
|
||||
}
|
||||
|
||||
export interface NormalizedSchema extends Schema {
|
||||
|
||||
@ -175,7 +175,14 @@
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"hidden": true
|
||||
},
|
||||
"bundler": {
|
||||
"description": "The bundler to use.",
|
||||
"enum": ["vite", "webpack"],
|
||||
"x-prompt": "Which bundler do you want to use?",
|
||||
"default": "webpack"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
"required": [],
|
||||
"examplesFile": "../../../docs/application-examples.md"
|
||||
}
|
||||
|
||||
@ -2,16 +2,11 @@ import { assertMinimumCypressVersion } from '@nrwl/cypress/src/utils/cypress-ver
|
||||
import {
|
||||
DependencyType,
|
||||
ProjectGraph,
|
||||
readJson,
|
||||
readProjectConfiguration,
|
||||
readTargetOptions,
|
||||
Tree,
|
||||
updateProjectConfiguration,
|
||||
} from '@nrwl/devkit';
|
||||
import {
|
||||
createTreeWithEmptyV1Workspace,
|
||||
createTreeWithEmptyWorkspace,
|
||||
} from '@nrwl/devkit/testing';
|
||||
import { createTreeWithEmptyWorkspace } from '@nrwl/devkit/testing';
|
||||
import { Linter } from '@nrwl/linter';
|
||||
import { applicationGenerator } from '../application/application';
|
||||
import { componentGenerator } from '../component/component';
|
||||
@ -45,6 +40,7 @@ describe('React:CypressComponentTestConfiguration', () => {
|
||||
style: 'scss',
|
||||
unitTestRunner: 'none',
|
||||
name: 'my-app',
|
||||
bundler: 'vite',
|
||||
});
|
||||
await libraryGenerator(tree, {
|
||||
linter: Linter.EsLint,
|
||||
@ -79,6 +75,7 @@ describe('React:CypressComponentTestConfiguration', () => {
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
await cypressComponentConfigGenerator(tree, {
|
||||
project: 'some-lib',
|
||||
generateTests: false,
|
||||
@ -115,6 +112,7 @@ describe('React:CypressComponentTestConfiguration', () => {
|
||||
style: 'scss',
|
||||
unitTestRunner: 'none',
|
||||
name: 'my-app',
|
||||
bundler: 'vite',
|
||||
});
|
||||
await libraryGenerator(tree, {
|
||||
linter: Linter.EsLint,
|
||||
@ -185,6 +183,7 @@ describe('React:CypressComponentTestConfiguration', () => {
|
||||
style: 'scss',
|
||||
unitTestRunner: 'none',
|
||||
name: 'my-app',
|
||||
bundler: 'vite',
|
||||
});
|
||||
await libraryGenerator(tree, {
|
||||
linter: Linter.EsLint,
|
||||
@ -232,6 +231,7 @@ describe('React:CypressComponentTestConfiguration', () => {
|
||||
style: 'scss',
|
||||
unitTestRunner: 'none',
|
||||
name: 'my-app',
|
||||
bundler: 'vite',
|
||||
});
|
||||
await libraryGenerator(tree, {
|
||||
linter: Linter.EsLint,
|
||||
@ -286,6 +286,7 @@ describe('React:CypressComponentTestConfiguration', () => {
|
||||
style: 'scss',
|
||||
unitTestRunner: 'none',
|
||||
name: 'my-app',
|
||||
bundler: 'vite',
|
||||
});
|
||||
await libraryGenerator(tree, {
|
||||
name: 'some-lib',
|
||||
@ -327,7 +328,7 @@ describe('React:CypressComponentTestConfiguration', () => {
|
||||
"Error trying to find build configuration. Try manually specifying the build target with the --build-target flag.
|
||||
Provided project? some-lib
|
||||
Provided build target? my-app:build
|
||||
Provided Executors? @nrwl/webpack:webpack"
|
||||
Provided Executors? @nrwl/webpack:webpack, @nrwl/vite:build"
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
@ -16,7 +16,10 @@ export async function updateProjectConfig(
|
||||
const found = await findBuildConfig(tree, {
|
||||
project: options.project,
|
||||
buildTarget: options.buildTarget,
|
||||
validExecutorNames: new Set<string>(['@nrwl/webpack:webpack']),
|
||||
validExecutorNames: new Set<string>([
|
||||
'@nrwl/webpack:webpack',
|
||||
'@nrwl/vite:build',
|
||||
]),
|
||||
});
|
||||
|
||||
assetValidConfig(found?.config);
|
||||
|
||||
@ -1,10 +1,7 @@
|
||||
import { formatFiles, Tree } from '@nrwl/devkit';
|
||||
|
||||
import applicationGenerator from '../application/application';
|
||||
import {
|
||||
normalizeOptions,
|
||||
normalizeProjectName,
|
||||
} from '../application/lib/normalize-options';
|
||||
import { normalizeOptions } from '../application/lib/normalize-options';
|
||||
import { updateModuleFederationProject } from '../../rules/update-module-federation-project';
|
||||
import { addModuleFederationFiles } from './lib/add-module-federation-files';
|
||||
import { updateModuleFederationE2eProject } from './lib/update-module-federation-e2e-project';
|
||||
@ -35,6 +32,7 @@ export async function hostGenerator(host: Tree, schema: Schema) {
|
||||
e2eTestRunner: options.e2eTestRunner,
|
||||
linter: options.linter,
|
||||
devServerPort: remotePort,
|
||||
bundler: options.bundler ?? 'webpack',
|
||||
});
|
||||
remotePort++;
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ export interface Schema {
|
||||
compiler?: 'babel' | 'swc';
|
||||
devServerPort?: number;
|
||||
remotes?: string[];
|
||||
bundler?: 'webpack' | 'vite';
|
||||
}
|
||||
|
||||
export interface NormalizedSchema extends Schema {
|
||||
|
||||
@ -148,6 +148,12 @@
|
||||
"devServerPort": {
|
||||
"type": "number",
|
||||
"description": "The port for the dev server of the remote app."
|
||||
},
|
||||
"bundler": {
|
||||
"description": "The bundler to use.",
|
||||
"enum": ["vite", "webpack"],
|
||||
"x-prompt": "Which bundler do you want to use?",
|
||||
"default": "webpack"
|
||||
}
|
||||
},
|
||||
"required": ["name"],
|
||||
|
||||
@ -453,6 +453,7 @@ describe('lib', () => {
|
||||
routing: true,
|
||||
style: 'css',
|
||||
standaloneConfig: false,
|
||||
bundler: 'webpack',
|
||||
});
|
||||
|
||||
await libraryGenerator(appTree, {
|
||||
@ -479,6 +480,7 @@ describe('lib', () => {
|
||||
name: 'myApp',
|
||||
style: 'css',
|
||||
standaloneConfig: false,
|
||||
bundler: 'webpack',
|
||||
});
|
||||
|
||||
await libraryGenerator(appTree, {
|
||||
|
||||
@ -32,6 +32,7 @@ export async function remoteGenerator(host: Tree, schema: Schema) {
|
||||
const initApp = await applicationGenerator(host, {
|
||||
...options,
|
||||
skipDefaultProject: true,
|
||||
bundler: schema.bundler ?? 'webpack',
|
||||
});
|
||||
|
||||
if (schema.host) {
|
||||
|
||||
@ -23,4 +23,5 @@ export interface Schema {
|
||||
compiler?: 'babel' | 'swc';
|
||||
host?: string;
|
||||
devServerPort?: number;
|
||||
bundler?: 'webpack' | 'vite';
|
||||
}
|
||||
|
||||
@ -152,6 +152,12 @@
|
||||
"devServerPort": {
|
||||
"type": "number",
|
||||
"description": "The port for the dev server of the remote app."
|
||||
},
|
||||
"bundler": {
|
||||
"description": "The bundler to use.",
|
||||
"enum": ["vite", "webpack"],
|
||||
"x-prompt": "Which bundler do you want to use?",
|
||||
"default": "webpack"
|
||||
}
|
||||
},
|
||||
"required": ["name"],
|
||||
|
||||
@ -66,6 +66,7 @@ export async function createTestUIApp(name: string): Promise<Tree> {
|
||||
unitTestRunner: 'none',
|
||||
name,
|
||||
standaloneConfig: false,
|
||||
bundler: 'vite',
|
||||
});
|
||||
|
||||
const config = readProjectConfiguration(tree, name);
|
||||
|
||||
32
packages/vite/docs/configuration-examples.md
Normal file
32
packages/vite/docs/configuration-examples.md
Normal file
@ -0,0 +1,32 @@
|
||||
This is a generator for setting up Vite configuration for an existing React or Web application. It will change the build and serve targets to use the `@nrwl/vite` executors for serving and building the application. This generator will modify your code, so make sure to commit your changes before running it.
|
||||
|
||||
```bash
|
||||
nx g @nrwl/vite:configuration
|
||||
```
|
||||
|
||||
When running this generator, you will be prompted to provide the following:
|
||||
|
||||
- The `project`, as the name of the project you want to generate the configuration for.
|
||||
- The `uiFramework` you want to use. Supported values are: `react` and `none`.
|
||||
|
||||
You must provide a `project` and a `uiFramework` for the generator to work.
|
||||
|
||||
You can read more about how this generator works, in the [Vite package overview page](/packages/vite).
|
||||
|
||||
## Examples
|
||||
|
||||
### Change a React app to use Vite
|
||||
|
||||
```bash
|
||||
nx g @nrwl/vite:configuration --project=my-app --uiFramework=react
|
||||
```
|
||||
|
||||
This will change the `my-app` project to use Vite instead of the default Webpack configuration. The changes this generator will do are described in the [Vite package overview page](/packages/vite).
|
||||
|
||||
### Change a Web app to use Vite
|
||||
|
||||
```bash
|
||||
nx g @nrwl/vite:configuration --project=my-app --uiFramework=none
|
||||
```
|
||||
|
||||
This will change the `my-app` project to use Vite instead of the existing bundler configuration.
|
||||
23
packages/vite/docs/init-examples.md
Normal file
23
packages/vite/docs/init-examples.md
Normal file
@ -0,0 +1,23 @@
|
||||
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).
|
||||
|
||||
You can use it on its own like this:
|
||||
|
||||
```bash
|
||||
nx g @nrwl/vite:configuration
|
||||
```
|
||||
|
||||
However, 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`.
|
||||
|
||||
## Examples
|
||||
|
||||
### Install all the necessary dependencies for Vite and the React plugin
|
||||
|
||||
```bash
|
||||
nx g @nrwl/vite:init --uiFramework=react
|
||||
```
|
||||
|
||||
### Install all the necessary dependencies for Vite
|
||||
|
||||
```bash
|
||||
nx g @nrwl/vite:init --uiFramework=none
|
||||
```
|
||||
@ -5,18 +5,32 @@
|
||||
"init": {
|
||||
"factory": "./src/generators/init/init#initSchematic",
|
||||
"schema": "./src/generators/init/schema.json",
|
||||
"description": "Add Storybook configuration to the workspace.",
|
||||
"description": "Initialize Vite in the workspace.",
|
||||
"aliases": ["ng-add"],
|
||||
"hidden": true
|
||||
},
|
||||
"configuration": {
|
||||
"factory": "./src/generators/configuration/configuration#configurationSchematic",
|
||||
"schema": "./src/generators/configuration/schema.json",
|
||||
"description": "Add Vite configuration to an application.",
|
||||
"aliases": ["ng-add"],
|
||||
"hidden": false
|
||||
}
|
||||
},
|
||||
"generators": {
|
||||
"init": {
|
||||
"factory": "./src/generators/init/init",
|
||||
"schema": "./src/generators/init/schema.json",
|
||||
"description": "Add Vite configuration to the workspace.",
|
||||
"description": "Initialize Vite in the workspace.",
|
||||
"aliases": ["ng-add"],
|
||||
"hidden": true
|
||||
},
|
||||
"configuration": {
|
||||
"factory": "./src/generators/configuration/configuration",
|
||||
"schema": "./src/generators/configuration/schema.json",
|
||||
"description": "Add Vite configuration to an application.",
|
||||
"aliases": ["ng-add"],
|
||||
"hidden": false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1 +1,2 @@
|
||||
export * from './src/utils/versions';
|
||||
export { viteConfigurationGenerator } from './src/generators/configuration/configuration';
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { ExecutorContext, logger } from '@nrwl/devkit';
|
||||
import { build, InlineConfig } from 'vite';
|
||||
import 'dotenv/config';
|
||||
import { getBuildConfig } from '../../utils/helper-functions';
|
||||
import { getBuildConfig } from '../../utils/options-utils';
|
||||
import { ViteBuildExecutorOptions } from './schema';
|
||||
import { copyAssets } from '@nrwl/js';
|
||||
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
import type { AssetGlob } from '@nrwl/workspace/src/utilities/assets';
|
||||
import type { FileReplacement } from '../../plugins/rollup-replace-files.plugin';
|
||||
export interface ViteBuildExecutorOptions {
|
||||
outputPath?: string;
|
||||
outputPath: string;
|
||||
baseHref?: string;
|
||||
proxyConfig?: string;
|
||||
tsConfig?: string;
|
||||
configFile?: string;
|
||||
assets: AssetGlob[];
|
||||
fileReplacements: FileReplacement[];
|
||||
assets?: AssetGlob[];
|
||||
fileReplacements?: FileReplacement[];
|
||||
sourcemap?: boolean | 'inline' | 'hidden';
|
||||
hmr?: boolean;
|
||||
}
|
||||
|
||||
@ -7,7 +7,7 @@ import {
|
||||
getBuildConfig,
|
||||
getBuildTargetOptions,
|
||||
getServerOptions,
|
||||
} from '../../utils/helper-functions';
|
||||
} from '../../utils/options-utils';
|
||||
|
||||
import { copyAssets, CopyAssetsResult } from '@nrwl/js';
|
||||
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
import type { FileReplacement } from '../../plugins/rollup-replace-files.plugin';
|
||||
export interface ViteDevServerExecutorOptions {
|
||||
buildTarget: string;
|
||||
baseHref?: string;
|
||||
proxyConfig?: string;
|
||||
fileReplacements: FileReplacement[];
|
||||
port?: number;
|
||||
host?: string | boolean;
|
||||
https?: boolean;
|
||||
|
||||
@ -8,7 +8,11 @@
|
||||
"presets": [
|
||||
{
|
||||
"name": "Default minimum setup",
|
||||
"keys": []
|
||||
"keys": ["buildTarget"]
|
||||
},
|
||||
{
|
||||
"name": "Using a Different Port",
|
||||
"keys": ["buildTarget", "port"]
|
||||
}
|
||||
],
|
||||
"properties": {
|
||||
@ -16,38 +20,11 @@
|
||||
"type": "string",
|
||||
"description": "Target which builds the application."
|
||||
},
|
||||
"baseHref": {
|
||||
"type": "string",
|
||||
"description": "Base url for the application being built."
|
||||
},
|
||||
"proxyConfig": {
|
||||
"type": "string",
|
||||
"description": "Path to the proxy configuration file.",
|
||||
"x-completion-type": "file"
|
||||
},
|
||||
"fileReplacements": {
|
||||
"description": "Replace files with other files in the build.",
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"replace": {
|
||||
"type": "string",
|
||||
"description": "The file to be replaced.",
|
||||
"x-completion-type": "file"
|
||||
},
|
||||
"with": {
|
||||
"type": "string",
|
||||
"description": "The file to replace with.",
|
||||
"x-completion-type": "file"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["replace", "with"]
|
||||
},
|
||||
"default": []
|
||||
},
|
||||
|
||||
"port": {
|
||||
"type": "number",
|
||||
"description": "Port to listen on.",
|
||||
@ -77,6 +54,6 @@
|
||||
}
|
||||
},
|
||||
"definitions": {},
|
||||
"required": [],
|
||||
"required": ["buildTarget"],
|
||||
"examplesFile": "../../../docs/dev-server-examples.md"
|
||||
}
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { ExecutorContext } from '@nrwl/devkit';
|
||||
import { File, Reporter } from 'vitest';
|
||||
import { normalizeConfigFilePath } from '../../utils/helper-functions';
|
||||
import { VitestExecutorSchema } from './schema';
|
||||
|
||||
class NxReporter implements Reporter {
|
||||
|
||||
@ -0,0 +1,94 @@
|
||||
import { addDependenciesToPackageJson, readJson, Tree } from '@nrwl/devkit';
|
||||
import { createTreeWithEmptyV1Workspace } from '@nrwl/devkit/testing';
|
||||
import { nxVersion } from '../../utils/versions';
|
||||
|
||||
import { viteConfigurationGenerator } from './configuration';
|
||||
import {
|
||||
mockReactAppGenerator,
|
||||
mockWebAppGenerator,
|
||||
} from '../../utils/test-utils';
|
||||
|
||||
describe('@nrwl/vite:configuration', () => {
|
||||
let tree: Tree;
|
||||
|
||||
describe('transform React app to use Vite', () => {
|
||||
beforeAll(async () => {
|
||||
tree = createTreeWithEmptyV1Workspace();
|
||||
await mockReactAppGenerator(tree);
|
||||
const existing = 'existing';
|
||||
const existingVersion = '1.0.0';
|
||||
addDependenciesToPackageJson(
|
||||
tree,
|
||||
{ '@nrwl/vite': nxVersion, [existing]: existingVersion },
|
||||
{ [existing]: existingVersion }
|
||||
);
|
||||
await viteConfigurationGenerator(tree, {
|
||||
uiFramework: 'react',
|
||||
project: 'my-test-react-app',
|
||||
});
|
||||
});
|
||||
it('should add vite packages and react-related dependencies for vite', async () => {
|
||||
const packageJson = readJson(tree, '/package.json');
|
||||
expect(packageJson.devDependencies).toMatchObject({
|
||||
vite: expect.any(String),
|
||||
'@vitejs/plugin-react': expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('should move index.html to the root of the project', () => {
|
||||
expect(tree.exists('apps/my-test-react-app/src/index.html')).toBeFalsy();
|
||||
expect(tree.exists('apps/my-test-react-app/index.html')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should create correct tsconfig compilerOptions', () => {
|
||||
const tsconfigJson = readJson(
|
||||
tree,
|
||||
'apps/my-test-react-app/tsconfig.json'
|
||||
);
|
||||
expect(tsconfigJson.compilerOptions.types).toMatchObject(['vite/client']);
|
||||
});
|
||||
|
||||
it('should create vite.config file at the root of the app', () => {
|
||||
expect(tree.exists('apps/my-test-react-app/vite.config.ts')).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('transform Web app to use Vite', () => {
|
||||
beforeAll(async () => {
|
||||
tree = createTreeWithEmptyV1Workspace();
|
||||
await mockWebAppGenerator(tree);
|
||||
const existing = 'existing';
|
||||
const existingVersion = '1.0.0';
|
||||
addDependenciesToPackageJson(
|
||||
tree,
|
||||
{ '@nrwl/vite': nxVersion, [existing]: existingVersion },
|
||||
{ [existing]: existingVersion }
|
||||
);
|
||||
await viteConfigurationGenerator(tree, {
|
||||
uiFramework: 'none',
|
||||
project: 'my-test-web-app',
|
||||
});
|
||||
});
|
||||
it('should add vite dependencies for vite', async () => {
|
||||
const packageJson = readJson(tree, '/package.json');
|
||||
expect(packageJson.devDependencies).toMatchObject({
|
||||
vite: expect.any(String),
|
||||
'vite-tsconfig-paths': expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('should move index.html to the root of the project', () => {
|
||||
expect(tree.exists('apps/my-test-web-app/src/index.html')).toBeFalsy();
|
||||
expect(tree.exists('apps/my-test-web-app/index.html')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should create correct tsconfig compilerOptions', () => {
|
||||
const tsconfigJson = readJson(tree, 'apps/my-test-web-app/tsconfig.json');
|
||||
expect(tsconfigJson.compilerOptions.types).toMatchObject(['vite/client']);
|
||||
});
|
||||
|
||||
it('should create vite.config file at the root of the app', () => {
|
||||
expect(tree.exists('apps/my-test-web-app/vite.config.ts')).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
52
packages/vite/src/generators/configuration/configuration.ts
Normal file
52
packages/vite/src/generators/configuration/configuration.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import {
|
||||
convertNxGenerator,
|
||||
formatFiles,
|
||||
GeneratorCallback,
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
} from '@nrwl/devkit';
|
||||
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
|
||||
import {
|
||||
findServeAndBuildTargets,
|
||||
addOrChangeBuildTarget,
|
||||
addOrChangeServeTarget,
|
||||
editTsConfig,
|
||||
moveAndEditIndexHtml,
|
||||
writeViteConfig,
|
||||
} from '../../utils/generator-utils';
|
||||
|
||||
import initGenerator from '../init/init';
|
||||
import { Schema } from './schema';
|
||||
|
||||
export async function viteConfigurationGenerator(tree: Tree, schema: Schema) {
|
||||
const tasks: GeneratorCallback[] = [];
|
||||
|
||||
const { targets } = readProjectConfiguration(tree, schema.project);
|
||||
let buildTarget = 'build';
|
||||
let serveTarget = 'serve';
|
||||
|
||||
if (!schema.newProject) {
|
||||
buildTarget = findServeAndBuildTargets(targets).buildTarget;
|
||||
serveTarget = findServeAndBuildTargets(targets).serveTarget;
|
||||
moveAndEditIndexHtml(tree, schema, buildTarget);
|
||||
editTsConfig(tree, schema);
|
||||
}
|
||||
|
||||
const initTask = await initGenerator(tree, {
|
||||
uiFramework: schema.uiFramework,
|
||||
});
|
||||
tasks.push(initTask);
|
||||
|
||||
addOrChangeBuildTarget(tree, schema, buildTarget);
|
||||
addOrChangeServeTarget(tree, schema, serveTarget);
|
||||
writeViteConfig(tree, schema);
|
||||
|
||||
await formatFiles(tree);
|
||||
|
||||
return runTasksInSerial(...tasks);
|
||||
}
|
||||
|
||||
export default viteConfigurationGenerator;
|
||||
export const configurationSchematic = convertNxGenerator(
|
||||
viteConfigurationGenerator
|
||||
);
|
||||
5
packages/vite/src/generators/configuration/schema.d.ts
vendored
Normal file
5
packages/vite/src/generators/configuration/schema.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
export interface Schema {
|
||||
uiFramework: 'react' | 'none';
|
||||
project: string;
|
||||
newProject?: boolean;
|
||||
}
|
||||
33
packages/vite/src/generators/configuration/schema.json
Normal file
33
packages/vite/src/generators/configuration/schema.json
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"cli": "nx",
|
||||
"title": "Add Vite Configuration to an application.",
|
||||
"description": "Add Vite Configuration to an application.",
|
||||
"$id": "configure-vite-app",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"project": {
|
||||
"type": "string",
|
||||
"description": "The name of the project.",
|
||||
"$default": {
|
||||
"$source": "argv",
|
||||
"index": 0
|
||||
},
|
||||
"aliases": ["name", "projectName"],
|
||||
"x-dropdown": "project",
|
||||
"x-prompt": "What is the name of the project to set up a webpack for?"
|
||||
},
|
||||
"uiFramework": {
|
||||
"type": "string",
|
||||
"description": "UI Framework to use for Vite.",
|
||||
"enum": ["react", "none"],
|
||||
"default": "none",
|
||||
"x-prompt": "What UI framework plugin should Vite use?"
|
||||
},
|
||||
"newProject": {
|
||||
"type": "boolean",
|
||||
"description": "Is this a new project?",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"examplesFile": "../../../docs/configuration-examples.md"
|
||||
}
|
||||
@ -5,6 +5,7 @@ import {
|
||||
Tree,
|
||||
updateJson,
|
||||
} from '@nrwl/devkit';
|
||||
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
|
||||
|
||||
import {
|
||||
nxVersion,
|
||||
@ -54,9 +55,8 @@ function moveToDevDependencies(tree: Tree) {
|
||||
}
|
||||
|
||||
export function initGenerator(tree: Tree, schema: Schema) {
|
||||
const installTask = checkDependenciesInstalled(tree, schema);
|
||||
moveToDevDependencies(tree);
|
||||
|
||||
const installTask = checkDependenciesInstalled(tree, schema);
|
||||
return installTask;
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{
|
||||
"cli": "nx",
|
||||
"title": "Add Vite Configuration to the workspace",
|
||||
"description": "Add Vite Configuration to the workspace.",
|
||||
"title": "Initialize Vite in the workspace.",
|
||||
"description": "Initialize Vite in the workspace.",
|
||||
"$id": "init-vite-plugin",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -12,5 +12,6 @@
|
||||
"default": "react",
|
||||
"x-prompt": "What UI framework plugin should Vite use?"
|
||||
}
|
||||
}
|
||||
},
|
||||
"examplesFile": "../../../docs/init-examples.md"
|
||||
}
|
||||
|
||||
354
packages/vite/src/utils/generator-utils.ts
Normal file
354
packages/vite/src/utils/generator-utils.ts
Normal file
@ -0,0 +1,354 @@
|
||||
import {
|
||||
joinPathFragments,
|
||||
logger,
|
||||
offsetFromRoot,
|
||||
readJson,
|
||||
readProjectConfiguration,
|
||||
TargetConfiguration,
|
||||
Tree,
|
||||
updateProjectConfiguration,
|
||||
writeJson,
|
||||
} from '@nrwl/devkit';
|
||||
import { ViteBuildExecutorOptions } from '../executors/build/schema';
|
||||
import { ViteDevServerExecutorOptions } from '../executors/dev-server/schema';
|
||||
import { Schema } from '../generators/configuration/schema';
|
||||
|
||||
/**
|
||||
* This function is used to find the build and serve targets for
|
||||
* an application.
|
||||
*
|
||||
* The reason this function exists is because we cannot assume
|
||||
* that the user has not created a custom build target for the application,
|
||||
* or that they have not changed the name of the build target
|
||||
* from build to anything else.
|
||||
*
|
||||
* So, in order to find the correct name of the target,
|
||||
* we have to look into all the targets, check the executor
|
||||
* they are using, and infer from the executor that the target
|
||||
* is a build target.
|
||||
*/
|
||||
export function findServeAndBuildTargets(targets: {
|
||||
[targetName: string]: TargetConfiguration;
|
||||
}): {
|
||||
buildTarget: string;
|
||||
serveTarget: string;
|
||||
} {
|
||||
const returnObject: {
|
||||
buildTarget: string;
|
||||
serveTarget: string;
|
||||
} = {
|
||||
buildTarget: 'build',
|
||||
serveTarget: 'serve',
|
||||
};
|
||||
|
||||
Object.entries(targets).forEach(([target, targetConfig]) => {
|
||||
switch (targetConfig.executor) {
|
||||
case '@nrwl/webpack:dev-server':
|
||||
case '@nxext/vite:dev':
|
||||
returnObject.serveTarget = target;
|
||||
break;
|
||||
case '@angular-devkit/build-angular:browser':
|
||||
/**
|
||||
* Not looking for '@nrwl/angular:ng-packagr-lite' or any other
|
||||
* angular executors.
|
||||
* Only looking for '@angular-devkit/build-angular:browser'
|
||||
* because the '@nrwl/angular:ng-packagr-lite' executor
|
||||
* (and maybe the other custom exeucutors) is only used for libs atm.
|
||||
*/
|
||||
returnObject.buildTarget = target;
|
||||
break;
|
||||
case '@nrwl/webpack:webpack':
|
||||
case '@nrwl/next:build':
|
||||
case '@nrwl/web:webpack':
|
||||
case '@nrwl/web:rollup':
|
||||
case '@nrwl/js:tsc':
|
||||
case '@nrwl/angular:ng-packagr-lite':
|
||||
case '@nrwl/js:babel':
|
||||
case '@nrwl/js:swc':
|
||||
case '@nxext/vite:build':
|
||||
returnObject.buildTarget = target;
|
||||
break;
|
||||
default:
|
||||
returnObject.buildTarget = 'build';
|
||||
returnObject.serveTarget = 'serve';
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return returnObject;
|
||||
}
|
||||
|
||||
export function addOrChangeBuildTarget(
|
||||
tree: Tree,
|
||||
options: Schema,
|
||||
target: string
|
||||
) {
|
||||
const project = readProjectConfiguration(tree, options.project);
|
||||
|
||||
const buildOptions: ViteBuildExecutorOptions = {
|
||||
outputPath: joinPathFragments(
|
||||
'dist',
|
||||
project.root != '.' ? project.root : options.project
|
||||
),
|
||||
};
|
||||
|
||||
const targets = {
|
||||
...project.targets,
|
||||
};
|
||||
|
||||
if (targets[target]) {
|
||||
targets[target].options = {
|
||||
...buildOptions,
|
||||
};
|
||||
targets[target].executor = '@nrwl/vite:build';
|
||||
} else {
|
||||
targets[`${target}`] = {
|
||||
executor: '@nrwl/vite:build',
|
||||
outputs: ['{options.outputPath}'],
|
||||
defaultConfiguration: 'production',
|
||||
options: buildOptions,
|
||||
configurations: {
|
||||
development: {},
|
||||
production: {
|
||||
fileReplacements: [
|
||||
{
|
||||
replace: joinPathFragments(
|
||||
project.sourceRoot,
|
||||
'environments/environment.ts'
|
||||
),
|
||||
with: joinPathFragments(
|
||||
project.sourceRoot,
|
||||
'environments/environment.prod.ts'
|
||||
),
|
||||
},
|
||||
],
|
||||
sourceMap: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
updateProjectConfiguration(tree, options.project, {
|
||||
...project,
|
||||
targets: {
|
||||
...targets,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function addOrChangeServeTarget(
|
||||
tree: Tree,
|
||||
options: Schema,
|
||||
target: string
|
||||
) {
|
||||
const project = readProjectConfiguration(tree, options.project);
|
||||
|
||||
const serveOptions: ViteDevServerExecutorOptions = {
|
||||
buildTarget: `${options.project}:build`,
|
||||
};
|
||||
|
||||
const targets = {
|
||||
...project.targets,
|
||||
};
|
||||
|
||||
if (targets[target]) {
|
||||
targets[target].options = {
|
||||
...serveOptions,
|
||||
};
|
||||
targets[target].executor = '@nrwl/vite:dev-server';
|
||||
} else {
|
||||
targets[`${target}`] = {
|
||||
executor: '@nrwl/vite:dev-server',
|
||||
defaultConfiguration: 'development',
|
||||
options: {
|
||||
buildTarget: `${options.project}:build`,
|
||||
},
|
||||
configurations: {
|
||||
development: {
|
||||
buildTarget: `${options.project}:build:development`,
|
||||
},
|
||||
production: {
|
||||
buildTarget: `${options.project}:build:production`,
|
||||
hmr: false,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
updateProjectConfiguration(tree, options.project, {
|
||||
...project,
|
||||
targets: {
|
||||
...targets,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function editTsConfig(tree: Tree, options: Schema) {
|
||||
const projectConfig = readProjectConfiguration(tree, options.project);
|
||||
|
||||
const config = readJson(tree, `${projectConfig.root}/tsconfig.json`);
|
||||
|
||||
switch (options.uiFramework) {
|
||||
case 'react':
|
||||
config.compilerOptions = {
|
||||
target: 'ESNext',
|
||||
useDefineForClassFields: true,
|
||||
lib: ['DOM', 'DOM.Iterable', 'ESNext'],
|
||||
allowJs: false,
|
||||
skipLibCheck: true,
|
||||
esModuleInterop: false,
|
||||
allowSyntheticDefaultImports: true,
|
||||
strict: true,
|
||||
forceConsistentCasingInFileNames: true,
|
||||
module: 'ESNext',
|
||||
moduleResolution: 'Node',
|
||||
resolveJsonModule: true,
|
||||
isolatedModules: true,
|
||||
noEmit: true,
|
||||
jsx: 'react-jsx',
|
||||
types: ['vite/client'],
|
||||
};
|
||||
config.include = [...config.include, 'src'];
|
||||
break;
|
||||
case 'none':
|
||||
config.compilerOptions = {
|
||||
target: 'ESNext',
|
||||
useDefineForClassFields: true,
|
||||
module: 'ESNext',
|
||||
lib: ['ESNext', 'DOM'],
|
||||
moduleResolution: 'Node',
|
||||
strict: true,
|
||||
resolveJsonModule: true,
|
||||
isolatedModules: true,
|
||||
esModuleInterop: true,
|
||||
noEmit: true,
|
||||
noUnusedLocals: true,
|
||||
noUnusedParameters: true,
|
||||
noImplicitReturns: true,
|
||||
skipLibCheck: true,
|
||||
types: ['vite/client'],
|
||||
};
|
||||
config.include = [...config.include, 'src'];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
writeJson(tree, `${projectConfig.root}/tsconfig.json`, config);
|
||||
}
|
||||
|
||||
export function moveAndEditIndexHtml(
|
||||
tree: Tree,
|
||||
options: Schema,
|
||||
buildTarget: string
|
||||
) {
|
||||
const projectConfig = readProjectConfiguration(tree, options.project);
|
||||
|
||||
let indexHtmlPath =
|
||||
projectConfig.targets[buildTarget].options?.index ??
|
||||
`${projectConfig.root}/src/index.html`;
|
||||
const mainPath = (
|
||||
projectConfig.targets[buildTarget].options?.main ??
|
||||
`${projectConfig.root}/src/main.ts${
|
||||
options.uiFramework === 'react' ? 'x' : ''
|
||||
}`
|
||||
).replace(projectConfig.root, '');
|
||||
|
||||
if (
|
||||
!tree.exists(indexHtmlPath) &&
|
||||
tree.exists(`${projectConfig.root}/index.html`)
|
||||
) {
|
||||
indexHtmlPath = `${projectConfig.root}/index.html`;
|
||||
}
|
||||
|
||||
if (tree.exists(indexHtmlPath)) {
|
||||
const indexHtmlContent = tree.read(indexHtmlPath, 'utf8');
|
||||
if (
|
||||
!indexHtmlContent.includes(
|
||||
`<script type="module" src="${mainPath}"></script>`
|
||||
)
|
||||
) {
|
||||
tree.write(
|
||||
`${projectConfig.root}/index.html`,
|
||||
indexHtmlContent.replace(
|
||||
'</body>',
|
||||
`<script type="module" src="${mainPath}"></script>
|
||||
</body>`
|
||||
)
|
||||
);
|
||||
|
||||
if (tree.exists(`${projectConfig.root}/src/index.html`))
|
||||
tree.delete(`${projectConfig.root}/src/index.html`);
|
||||
}
|
||||
} else {
|
||||
tree.write(
|
||||
`${projectConfig.root}/index.html`,
|
||||
`<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="${mainPath}"></script>
|
||||
</body>
|
||||
</html>`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function writeViteConfig(tree: Tree, options: Schema) {
|
||||
const projectConfig = readProjectConfiguration(tree, options.project);
|
||||
|
||||
const viteConfigPath = `${projectConfig.root}/vite.config.ts`;
|
||||
|
||||
if (tree.exists(viteConfigPath)) {
|
||||
// TODO (katerina): Ideally we should check if the config is already set up correctly
|
||||
logger.info(
|
||||
`vite.config.ts already exists. Skipping creation of vite config for ${options.project}.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let viteConfigContent = '';
|
||||
|
||||
switch (options.uiFramework) {
|
||||
case 'react':
|
||||
viteConfigContent = `
|
||||
import { defineConfig } from 'vite';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import ViteTsConfigPathsPlugin from 'vite-tsconfig-paths';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
react(),
|
||||
ViteTsConfigPathsPlugin({
|
||||
root: '${offsetFromRoot(projectConfig.root)}',
|
||||
projects: ['tsconfig.base.json'],
|
||||
}),
|
||||
],
|
||||
});`;
|
||||
break;
|
||||
case 'none':
|
||||
viteConfigContent = `
|
||||
import { defineConfig } from 'vite';
|
||||
import ViteTsConfigPathsPlugin from 'vite-tsconfig-paths';
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
ViteTsConfigPathsPlugin({
|
||||
root: '${offsetFromRoot(projectConfig.root)}',
|
||||
projects: ['tsconfig.base.json'],
|
||||
}),
|
||||
],
|
||||
});`;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
tree.write(viteConfigPath, viteConfigContent);
|
||||
}
|
||||
85
packages/vite/src/utils/test-files/react-project.config.json
Normal file
85
packages/vite/src/utils/test-files/react-project.config.json
Normal file
@ -0,0 +1,85 @@
|
||||
{
|
||||
"name": "my-test-react-app",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"root": "apps/my-test-react-app",
|
||||
"sourceRoot": "apps/my-test-react-app/src",
|
||||
"projectType": "application",
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nrwl/webpack:webpack",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"defaultConfiguration": "production",
|
||||
"options": {
|
||||
"compiler": "babel",
|
||||
"outputPath": "dist/apps/my-test-react-app",
|
||||
"index": "apps/my-test-react-app/src/index.html",
|
||||
"baseHref": "/",
|
||||
"main": "apps/my-test-react-app/src/main.tsx",
|
||||
"polyfills": "apps/my-test-react-app/src/polyfills.ts",
|
||||
"tsConfig": "apps/my-test-react-app/tsconfig.app.json",
|
||||
"assets": [
|
||||
"apps/my-test-react-app/src/favicon.ico",
|
||||
"apps/my-test-react-app/src/assets"
|
||||
],
|
||||
"styles": ["apps/my-test-react-app/src/styles.css"],
|
||||
"scripts": [],
|
||||
"webpackConfig": "@nrwl/react/plugins/webpack"
|
||||
},
|
||||
"configurations": {
|
||||
"development": {
|
||||
"extractLicenses": false,
|
||||
"optimization": false,
|
||||
"sourceMap": true,
|
||||
"vendorChunk": true
|
||||
},
|
||||
"production": {
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "apps/my-test-react-app/src/environments/environment.ts",
|
||||
"with": "apps/my-test-react-app/src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"namedChunks": false,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"executor": "@nrwl/webpack:dev-server",
|
||||
"defaultConfiguration": "development",
|
||||
"options": {
|
||||
"buildTarget": "my-test-react-app:build",
|
||||
"hmr": true
|
||||
},
|
||||
"configurations": {
|
||||
"development": {
|
||||
"buildTarget": "my-test-react-app:build:development"
|
||||
},
|
||||
"production": {
|
||||
"buildTarget": "my-test-react-app:build:production",
|
||||
"hmr": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nrwl/linter:eslint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["apps/my-test-react-app/**/*.{ts,tsx,js,jsx}"]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nrwl/jest:jest",
|
||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||
"options": {
|
||||
"jestConfig": "apps/my-test-react-app/jest.config.ts",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": []
|
||||
}
|
||||
72
packages/vite/src/utils/test-files/web-project.config.json
Normal file
72
packages/vite/src/utils/test-files/web-project.config.json
Normal file
@ -0,0 +1,72 @@
|
||||
{
|
||||
"name": "my-test-web-app",
|
||||
"$schema": "../../node_modules/nx/schemas/project-schema.json",
|
||||
"projectType": "application",
|
||||
"root": "apps/my-test-web-app",
|
||||
"sourceRoot": "apps/my-test-web-app/src",
|
||||
"tags": [],
|
||||
"targets": {
|
||||
"build": {
|
||||
"executor": "@nrwl/webpack:webpack",
|
||||
"outputs": ["{options.outputPath}"],
|
||||
"defaultConfiguration": "production",
|
||||
"options": {
|
||||
"outputPath": "dist/apps/my-test-web-app",
|
||||
"compiler": "babel",
|
||||
"main": "apps/my-test-web-app/src/main.ts",
|
||||
"tsConfig": "apps/my-test-web-app/tsconfig.app.json",
|
||||
"assets": [
|
||||
"apps/my-test-web-app/src/favicon.ico",
|
||||
"apps/my-test-web-app/src/assets"
|
||||
],
|
||||
"index": "apps/my-test-web-app/src/index.html",
|
||||
"baseHref": "/",
|
||||
"polyfills": "apps/my-test-web-app/src/polyfills.ts",
|
||||
"styles": ["apps/my-test-web-app/src/styles.css"],
|
||||
"scripts": []
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"optimization": true,
|
||||
"outputHashing": "all",
|
||||
"sourceMap": false,
|
||||
"namedChunks": false,
|
||||
"extractLicenses": true,
|
||||
"vendorChunk": false,
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "apps/my-test-web-app/src/environments/environment.ts",
|
||||
"with": "apps/my-test-web-app/src/environments/environment.prod.ts"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"serve": {
|
||||
"executor": "@nrwl/webpack:dev-server",
|
||||
"options": {
|
||||
"buildTarget": "my-test-web-app:build"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"buildTarget": "my-test-web-app:build:production"
|
||||
}
|
||||
}
|
||||
},
|
||||
"lint": {
|
||||
"executor": "@nrwl/linter:eslint",
|
||||
"outputs": ["{options.outputFile}"],
|
||||
"options": {
|
||||
"lintFilePatterns": ["apps/my-test-web-app/**/*.ts"]
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"executor": "@nrwl/jest:jest",
|
||||
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],
|
||||
"options": {
|
||||
"jestConfig": "apps/my-test-web-app/jest.config.ts",
|
||||
"passWithNoTests": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
148
packages/vite/src/utils/test-utils.ts
Normal file
148
packages/vite/src/utils/test-utils.ts
Normal file
@ -0,0 +1,148 @@
|
||||
import { Tree, writeJson } from '@nrwl/devkit';
|
||||
import * as reactAppConfig from './test-files/react-project.config.json';
|
||||
import * as webAppConfig from './test-files/web-project.config.json';
|
||||
|
||||
export function mockReactAppGenerator(tree: Tree): Tree {
|
||||
const appName = 'my-test-react-app';
|
||||
|
||||
tree.write(
|
||||
`apps/${appName}/src/main.tsx`,
|
||||
`import ReactDOM from 'react-dom';\n`
|
||||
);
|
||||
|
||||
tree.write(
|
||||
`apps/${appName}/tsconfig.json`,
|
||||
`{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"allowJs": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
"noImplicitOverride": true,
|
||||
"noPropertyAccessFromIndexSignature": true,
|
||||
"noImplicitReturns": true,
|
||||
"noFallthroughCasesInSwitch": true
|
||||
},
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
tree.write(
|
||||
`apps/${appName}/src/index.html`,
|
||||
`<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>My Test React App</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>
|
||||
</body>
|
||||
</html>`
|
||||
);
|
||||
|
||||
writeJson(
|
||||
tree,
|
||||
'workspace.json',
|
||||
|
||||
{
|
||||
projects: {
|
||||
'my-test-react-app': {
|
||||
...reactAppConfig,
|
||||
root: `apps/${appName}`,
|
||||
projectType: 'application',
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
writeJson(tree, `apps/${appName}/project.json`, {
|
||||
...reactAppConfig,
|
||||
root: `apps/${appName}`,
|
||||
projectType: 'application',
|
||||
});
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
export function mockWebAppGenerator(tree: Tree): Tree {
|
||||
const appName = 'my-test-web-app';
|
||||
|
||||
tree.write(`apps/${appName}/src/main.ts`, `import './app/app.element.ts';`);
|
||||
|
||||
tree.write(
|
||||
`apps/${appName}/tsconfig.json`,
|
||||
`{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
tree.write(
|
||||
`apps/${appName}/src/index.html`,
|
||||
`<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>WebappPure</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>
|
||||
<workspace-root></workspace-root>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
);
|
||||
|
||||
writeJson(
|
||||
tree,
|
||||
'workspace.json',
|
||||
|
||||
{
|
||||
projects: {
|
||||
'my-test-web-app': {
|
||||
...webAppConfig,
|
||||
root: `apps/${appName}`,
|
||||
projectType: 'application',
|
||||
},
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
writeJson(tree, `apps/${appName}/project.json`, {
|
||||
...webAppConfig,
|
||||
root: `apps/${appName}`,
|
||||
projectType: 'application',
|
||||
});
|
||||
return tree;
|
||||
}
|
||||
43
packages/web/docs/application-examples.md
Normal file
43
packages/web/docs/application-examples.md
Normal file
@ -0,0 +1,43 @@
|
||||
## Examples
|
||||
|
||||
{% tabs %}
|
||||
{% tab label="Simple Application" %}
|
||||
|
||||
Create an application named `my-app`:
|
||||
|
||||
```bash
|
||||
nx g @nrwl/web:application my-app
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Application using Vite as bundler" %}
|
||||
|
||||
Create an application named `my-app`:
|
||||
|
||||
```bash
|
||||
nx g @nrwl/web:app my-app --bundler=vite
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Specify directory" %}
|
||||
|
||||
Create an application named `my-app` in the `my-dir` directory:
|
||||
|
||||
```bash
|
||||
nx g @nrwl/web:app my-app --directory=my-dir
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
|
||||
{% tab label="Add tags" %}
|
||||
|
||||
Add tags to the application (used for linting).
|
||||
|
||||
```bash
|
||||
nx g @nrwl/web:app my-app --tags=scope:admin,type:ui
|
||||
```
|
||||
|
||||
{% /tab %}
|
||||
{% /tabs %}
|
||||
@ -43,6 +43,7 @@
|
||||
"@nrwl/js": "file:../js",
|
||||
"@nrwl/linter": "file:../linter",
|
||||
"@nrwl/rollup": "file:../rollup",
|
||||
"@nrwl/vite": "file:../vite",
|
||||
"@nrwl/webpack": "file:../webpack",
|
||||
"@nrwl/workspace": "file:../workspace",
|
||||
"babel-plugin-const-enum": "^1.0.1",
|
||||
|
||||
@ -140,6 +140,36 @@ describe('app', () => {
|
||||
`);
|
||||
});
|
||||
|
||||
it('should generate files if bundler is vite', async () => {
|
||||
await applicationGenerator(tree, {
|
||||
name: 'myApp',
|
||||
standaloneConfig: false,
|
||||
bundler: 'vite',
|
||||
});
|
||||
expect(tree.exists('apps/my-app/src/main.ts')).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/src/app/app.element.ts')).toBeTruthy();
|
||||
expect(
|
||||
tree.exists('apps/my-app/src/app/app.element.spec.ts')
|
||||
).toBeTruthy();
|
||||
expect(tree.exists('apps/my-app/src/app/app.element.css')).toBeTruthy();
|
||||
|
||||
const tsconfig = readJson(tree, 'apps/my-app/tsconfig.json');
|
||||
expect(tsconfig.extends).toBe('../../tsconfig.base.json');
|
||||
expect(tsconfig.references).toEqual([
|
||||
{
|
||||
path: './tsconfig.app.json',
|
||||
},
|
||||
{
|
||||
path: './tsconfig.spec.json',
|
||||
},
|
||||
]);
|
||||
expect(tsconfig.compilerOptions.types).toMatchObject(['vite/client']);
|
||||
|
||||
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/vite.config.ts')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should extend from root tsconfig.json when no tsconfig.base.json', async () => {
|
||||
tree.rename('tsconfig.base.json', 'tsconfig.json');
|
||||
|
||||
@ -345,6 +375,39 @@ describe('app', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should setup the nrwl vite:build builder if bundler is vite', async () => {
|
||||
await applicationGenerator(tree, {
|
||||
name: 'my-App',
|
||||
standaloneConfig: false,
|
||||
bundler: 'vite',
|
||||
});
|
||||
const workspaceJson = readJson(tree, 'workspace.json');
|
||||
const architectConfig = workspaceJson.projects['my-app'].architect;
|
||||
expect(architectConfig.build.builder).toEqual('@nrwl/vite:build');
|
||||
expect(architectConfig.build.outputs).toEqual(['{options.outputPath}']);
|
||||
expect(architectConfig.build.options).toEqual({
|
||||
outputPath: 'dist/apps/my-app',
|
||||
});
|
||||
});
|
||||
|
||||
it('should setup the nrwl vite:dev-server builder if bundler is vite', async () => {
|
||||
await applicationGenerator(tree, {
|
||||
name: 'my-App',
|
||||
standaloneConfig: false,
|
||||
bundler: 'vite',
|
||||
});
|
||||
const workspaceJson = readJson(tree, 'workspace.json');
|
||||
const architectConfig = workspaceJson.projects['my-app'].architect;
|
||||
expect(architectConfig.serve.builder).toEqual('@nrwl/vite:dev-server');
|
||||
expect(architectConfig.serve.options).toEqual({
|
||||
buildTarget: 'my-app:build',
|
||||
});
|
||||
expect(architectConfig.serve.configurations.production).toEqual({
|
||||
buildTarget: 'my-app:build:production',
|
||||
hmr: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should setup the eslint builder', async () => {
|
||||
await applicationGenerator(tree, {
|
||||
name: 'my-App',
|
||||
@ -467,4 +530,42 @@ describe('app', () => {
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setup web app with --bundler=vite', () => {
|
||||
let viteAppTree: Tree;
|
||||
beforeAll(async () => {
|
||||
viteAppTree = createTreeWithEmptyV1Workspace();
|
||||
await applicationGenerator(viteAppTree, {
|
||||
name: 'myApp',
|
||||
bundler: 'vite',
|
||||
});
|
||||
});
|
||||
|
||||
it('should setup targets with vite configuration', () => {
|
||||
const workspaceJson = getProjects(viteAppTree);
|
||||
const targetConfig = workspaceJson.get('my-app').targets;
|
||||
expect(targetConfig.build.executor).toEqual('@nrwl/vite:build');
|
||||
expect(targetConfig.serve.executor).toEqual('@nrwl/vite:dev-server');
|
||||
expect(targetConfig.serve.options).toEqual({
|
||||
buildTarget: 'my-app:build',
|
||||
});
|
||||
});
|
||||
it('should add dependencies in package.json', () => {
|
||||
const packageJson = readJson(viteAppTree, '/package.json');
|
||||
|
||||
expect(packageJson.devDependencies).toMatchObject({
|
||||
vite: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('should create correct tsconfig compilerOptions', () => {
|
||||
const tsconfigJson = readJson(viteAppTree, '/apps/my-app/tsconfig.json');
|
||||
expect(tsconfigJson.compilerOptions.types).toMatchObject(['vite/client']);
|
||||
});
|
||||
|
||||
it('should create index.html and vite.config file at the root of the app', () => {
|
||||
expect(viteAppTree.exists('/apps/my-app/index.html')).toBe(true);
|
||||
expect(viteAppTree.exists('/apps/my-app/vite.config.ts')).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -24,6 +24,7 @@ import { swcCoreVersion } from '@nrwl/js/src/utils/versions';
|
||||
import { Linter, lintProjectGenerator } from '@nrwl/linter';
|
||||
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
|
||||
import { getRelativePathToRootTsConfig } from '@nrwl/workspace/src/utilities/typescript';
|
||||
import { viteConfigurationGenerator } from '@nrwl/vite';
|
||||
|
||||
import { swcLoaderVersion } from '../../utils/versions';
|
||||
import { webInitGenerator } from '../init/init';
|
||||
@ -38,16 +39,24 @@ interface NormalizedSchema extends Schema {
|
||||
}
|
||||
|
||||
function createApplicationFiles(tree: Tree, options: NormalizedSchema) {
|
||||
generateFiles(tree, join(__dirname, './files/app'), options.appProjectRoot, {
|
||||
...options,
|
||||
...names(options.name),
|
||||
tmpl: '',
|
||||
offsetFromRoot: offsetFromRoot(options.appProjectRoot),
|
||||
rootTsConfigPath: getRelativePathToRootTsConfig(
|
||||
tree,
|
||||
options.appProjectRoot
|
||||
generateFiles(
|
||||
tree,
|
||||
join(
|
||||
__dirname,
|
||||
options.bundler === 'vite' ? './files/app-vite' : './files/app'
|
||||
),
|
||||
});
|
||||
options.appProjectRoot,
|
||||
{
|
||||
...options,
|
||||
...names(options.name),
|
||||
tmpl: '',
|
||||
offsetFromRoot: offsetFromRoot(options.appProjectRoot),
|
||||
rootTsConfigPath: getRelativePathToRootTsConfig(
|
||||
tree,
|
||||
options.appProjectRoot
|
||||
),
|
||||
}
|
||||
);
|
||||
if (options.unitTestRunner === 'none') {
|
||||
tree.delete(join(options.appProjectRoot, './src/app/app.element.spec.ts'));
|
||||
}
|
||||
@ -143,7 +152,9 @@ async function addProject(tree: Tree, options: NormalizedSchema) {
|
||||
options.standaloneConfig
|
||||
);
|
||||
|
||||
await setupBundler(tree, options);
|
||||
if (options.bundler !== 'vite') {
|
||||
await setupBundler(tree, options);
|
||||
}
|
||||
|
||||
const workspace = readWorkspaceConfiguration(tree);
|
||||
|
||||
@ -187,6 +198,15 @@ export async function applicationGenerator(host: Tree, schema: Schema) {
|
||||
createApplicationFiles(host, options);
|
||||
await addProject(host, options);
|
||||
|
||||
if (options.bundler === 'vite') {
|
||||
const viteTask = await viteConfigurationGenerator(host, {
|
||||
uiFramework: 'react',
|
||||
project: options.projectName,
|
||||
newProject: true,
|
||||
});
|
||||
tasks.push(viteTask);
|
||||
}
|
||||
|
||||
const lintTask = await lintProjectGenerator(host, {
|
||||
linter: options.linter,
|
||||
project: options.projectName,
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
{
|
||||
"presets": [
|
||||
"@nrwl/web/babel"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
# This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
#
|
||||
# If you need to support different browsers in production, you may tweak the list below.
|
||||
|
||||
last 1 Chrome version
|
||||
last 1 Firefox version
|
||||
last 2 Edge major versions
|
||||
last 2 Safari major version
|
||||
last 2 iOS major versions
|
||||
Firefox ESR
|
||||
not IE 9-11 # For IE 9-11 support, remove 'not'.
|
||||
@ -0,0 +1,15 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title><%= className %></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>
|
||||
<<%= prefix %>-root></<%= prefix %>-root>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,416 @@
|
||||
/*
|
||||
* Remove template code below
|
||||
*/
|
||||
html {
|
||||
-webkit-text-size-adjust: 100%;
|
||||
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont,
|
||||
'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif,
|
||||
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
|
||||
'Noto Color Emoji';
|
||||
line-height: 1.5;
|
||||
tab-size: 4;
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
body {
|
||||
font-family: inherit;
|
||||
line-height: inherit;
|
||||
margin: 0;
|
||||
}
|
||||
h1,
|
||||
h2,
|
||||
p,
|
||||
pre {
|
||||
margin: 0;
|
||||
}
|
||||
*,
|
||||
::before,
|
||||
::after {
|
||||
box-sizing: border-box;
|
||||
border-width: 0;
|
||||
border-style: solid;
|
||||
border-color: currentColor;
|
||||
}
|
||||
h1,
|
||||
h2 {
|
||||
font-size: inherit;
|
||||
font-weight: inherit;
|
||||
}
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
pre {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
||||
'Liberation Mono', 'Courier New', monospace;
|
||||
}
|
||||
svg {
|
||||
display: block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
svg {
|
||||
shape-rendering: auto;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
pre {
|
||||
background-color: rgba(55, 65, 81, 1);
|
||||
border-radius: 0.25rem;
|
||||
color: rgba(229, 231, 235, 1);
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
|
||||
'Liberation Mono', 'Courier New', monospace;
|
||||
overflow: scroll;
|
||||
padding: 0.5rem 0.75rem;
|
||||
}
|
||||
|
||||
.shadow {
|
||||
box-shadow: 0 0 #0000, 0 0 #0000, 0 10px 15px -3px rgba(0, 0, 0, 0.1),
|
||||
0 4px 6px -2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.rounded {
|
||||
border-radius: 1.5rem;
|
||||
}
|
||||
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
}
|
||||
.container {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
max-width: 768px;
|
||||
padding-bottom: 3rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
color: rgba(55, 65, 81, 1);
|
||||
width: 100%;
|
||||
}
|
||||
#welcome {
|
||||
margin-top: 2.5rem;
|
||||
}
|
||||
#welcome h1 {
|
||||
font-size: 3rem;
|
||||
font-weight: 500;
|
||||
letter-spacing: -0.025em;
|
||||
line-height: 1;
|
||||
}
|
||||
#welcome span {
|
||||
display: block;
|
||||
font-size: 1.875rem;
|
||||
font-weight: 300;
|
||||
line-height: 2.25rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
#hero {
|
||||
align-items: center;
|
||||
background-color: hsla(214, 62%, 21%, 1);
|
||||
border: none;
|
||||
box-sizing: border-box;
|
||||
color: rgba(55, 65, 81, 1);
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
margin-top: 3.5rem;
|
||||
}
|
||||
#hero .text-container {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
padding: 3rem 2rem;
|
||||
}
|
||||
#hero .text-container h2 {
|
||||
font-size: 1.5rem;
|
||||
line-height: 2rem;
|
||||
position: relative;
|
||||
}
|
||||
#hero .text-container h2 svg {
|
||||
color: hsla(162, 47%, 50%, 1);
|
||||
height: 2rem;
|
||||
left: -0.25rem;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
width: 2rem;
|
||||
}
|
||||
#hero .text-container h2 span {
|
||||
margin-left: 2.5rem;
|
||||
}
|
||||
#hero .text-container a {
|
||||
background-color: rgba(255, 255, 255, 1);
|
||||
border-radius: 0.75rem;
|
||||
color: rgba(55, 65, 81, 1);
|
||||
display: inline-block;
|
||||
margin-top: 1.5rem;
|
||||
padding: 1rem 2rem;
|
||||
text-decoration: inherit;
|
||||
}
|
||||
#hero .logo-container {
|
||||
display: none;
|
||||
justify-content: center;
|
||||
padding-left: 2rem;
|
||||
padding-right: 2rem;
|
||||
}
|
||||
#hero .logo-container svg {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
width: 66.666667%;
|
||||
}
|
||||
|
||||
#middle-content {
|
||||
align-items: flex-start;
|
||||
display: grid;
|
||||
gap: 4rem;
|
||||
grid-template-columns: 1fr;
|
||||
margin-top: 3.5rem;
|
||||
}
|
||||
|
||||
#learning-materials {
|
||||
padding: 2.5rem 2rem;
|
||||
}
|
||||
#learning-materials h2 {
|
||||
font-weight: 500;
|
||||
font-size: 1.25rem;
|
||||
letter-spacing: -0.025em;
|
||||
line-height: 1.75rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
.list-item-link {
|
||||
align-items: center;
|
||||
border-radius: 0.75rem;
|
||||
display: flex;
|
||||
margin-top: 1rem;
|
||||
padding: 1rem;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
width: 100%;
|
||||
}
|
||||
.list-item-link svg:first-child {
|
||||
margin-right: 1rem;
|
||||
height: 1.5rem;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
width: 1.5rem;
|
||||
}
|
||||
.list-item-link > span {
|
||||
flex-grow: 1;
|
||||
font-weight: 400;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
.list-item-link > span > span {
|
||||
color: rgba(107, 114, 128, 1);
|
||||
display: block;
|
||||
flex-grow: 1;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 300;
|
||||
line-height: 1rem;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
.list-item-link svg:last-child {
|
||||
height: 1rem;
|
||||
transition-property: all;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
width: 1rem;
|
||||
}
|
||||
.list-item-link:hover {
|
||||
color: rgba(255, 255, 255, 1);
|
||||
background-color: hsla(162, 47%, 50%, 1);
|
||||
}
|
||||
.list-item-link:hover > span {
|
||||
}
|
||||
.list-item-link:hover > span > span {
|
||||
color: rgba(243, 244, 246, 1);
|
||||
}
|
||||
.list-item-link:hover svg:last-child {
|
||||
transform: translateX(0.25rem);
|
||||
}
|
||||
|
||||
#other-links {
|
||||
}
|
||||
.button-pill {
|
||||
padding: 1.5rem 2rem;
|
||||
transition-duration: 300ms;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
.button-pill svg {
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
flex-shrink: 0;
|
||||
width: 3rem;
|
||||
}
|
||||
.button-pill > span {
|
||||
letter-spacing: -0.025em;
|
||||
font-weight: 400;
|
||||
font-size: 1.125rem;
|
||||
line-height: 1.75rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
.button-pill span span {
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 300;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
.button-pill:hover svg,
|
||||
.button-pill:hover {
|
||||
color: rgba(255, 255, 255, 1) !important;
|
||||
}
|
||||
#nx-console:hover {
|
||||
background-color: rgba(0, 122, 204, 1);
|
||||
}
|
||||
#nx-console svg {
|
||||
color: rgba(0, 122, 204, 1);
|
||||
}
|
||||
|
||||
#nx-repo:hover {
|
||||
background-color: rgba(24, 23, 23, 1);
|
||||
}
|
||||
#nx-repo svg {
|
||||
color: rgba(24, 23, 23, 1);
|
||||
}
|
||||
|
||||
#nx-cloud {
|
||||
margin-bottom: 2rem;
|
||||
margin-top: 2rem;
|
||||
padding: 2.5rem 2rem;
|
||||
}
|
||||
#nx-cloud > div {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
#nx-cloud > div svg {
|
||||
border-radius: 0.375rem;
|
||||
flex-shrink: 0;
|
||||
width: 3rem;
|
||||
}
|
||||
#nx-cloud > div h2 {
|
||||
font-size: 1.125rem;
|
||||
font-weight: 400;
|
||||
letter-spacing: -0.025em;
|
||||
line-height: 1.75rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
#nx-cloud > div h2 span {
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 300;
|
||||
line-height: 1.25rem;
|
||||
}
|
||||
#nx-cloud p {
|
||||
font-size: 1rem;
|
||||
line-height: 1.5rem;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
#nx-cloud pre {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
#nx-cloud a {
|
||||
color: rgba(107, 114, 128, 1);
|
||||
display: block;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
margin-top: 1.5rem;
|
||||
text-align: right;
|
||||
}
|
||||
#nx-cloud a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#commands {
|
||||
padding: 2.5rem 2rem;
|
||||
|
||||
margin-top: 3.5rem;
|
||||
}
|
||||
#commands h2 {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 400;
|
||||
letter-spacing: -0.025em;
|
||||
line-height: 1.75rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
#commands p {
|
||||
font-size: 1rem;
|
||||
font-weight: 300;
|
||||
line-height: 1.5rem;
|
||||
margin-top: 1rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
details {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
margin-top: 1rem;
|
||||
padding-left: 1rem;
|
||||
padding-right: 1rem;
|
||||
width: 100%;
|
||||
}
|
||||
details pre > span {
|
||||
color: rgba(181, 181, 181, 1);
|
||||
}
|
||||
summary {
|
||||
border-radius: 0.5rem;
|
||||
display: flex;
|
||||
font-weight: 400;
|
||||
padding: 0.5rem;
|
||||
cursor: pointer;
|
||||
transition-property: background-color, border-color, color, fill, stroke,
|
||||
opacity, box-shadow, transform, filter, backdrop-filter,
|
||||
-webkit-backdrop-filter;
|
||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||
transition-duration: 150ms;
|
||||
}
|
||||
summary:hover {
|
||||
background-color: rgba(243, 244, 246, 1);
|
||||
}
|
||||
summary svg {
|
||||
height: 1.5rem;
|
||||
margin-right: 1rem;
|
||||
width: 1.5rem;
|
||||
}
|
||||
|
||||
#love {
|
||||
color: rgba(107, 114, 128, 1);
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.25rem;
|
||||
margin-top: 3.5rem;
|
||||
opacity: 0.6;
|
||||
text-align: center;
|
||||
}
|
||||
#love svg {
|
||||
color: rgba(252, 165, 165, 1);
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
display: inline;
|
||||
margin-top: -0.25rem;
|
||||
}
|
||||
|
||||
@media screen and (min-width: 768px) {
|
||||
#hero {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
#hero .logo-container {
|
||||
display: flex;
|
||||
}
|
||||
#middle-content {
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
import { AppElement } from './app.element';
|
||||
|
||||
describe('AppElement', () => {
|
||||
let app: AppElement;
|
||||
|
||||
beforeEach(() => {
|
||||
app = new AppElement();
|
||||
});
|
||||
|
||||
it('should create successfully', () => {
|
||||
expect(app).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should have a greeting', () => {
|
||||
app.connectedCallback();
|
||||
|
||||
expect(app.querySelector('h1').innerHTML).toContain(
|
||||
'Welcome <%= projectName %>'
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,385 @@
|
||||
import './app.element.<%= style %>';
|
||||
|
||||
export class AppElement extends HTMLElement {
|
||||
public static observedAttributes = [
|
||||
|
||||
];
|
||||
|
||||
connectedCallback() {
|
||||
const title = '<%= projectName %>';
|
||||
this.innerHTML = `
|
||||
<div class="wrapper">
|
||||
<div class="container">
|
||||
<!-- WELCOME -->
|
||||
<div id="welcome">
|
||||
<h1>
|
||||
<span> Hello there, </span>
|
||||
Welcome ${title} 👋
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<!-- HERO -->
|
||||
<div id="hero" class="rounded">
|
||||
<div class="text-container">
|
||||
<h2>
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z"
|
||||
/>
|
||||
</svg>
|
||||
<span>You're up and running</span>
|
||||
</h2>
|
||||
<a href="#commands"> What's next? </a>
|
||||
</div>
|
||||
<div class="logo-container">
|
||||
<svg
|
||||
fill="currentColor"
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M11.987 14.138l-3.132 4.923-5.193-8.427-.012 8.822H0V4.544h3.691l5.247 8.833.005-3.998 3.044 4.759zm.601-5.761c.024-.048 0-3.784.008-3.833h-3.65c.002.059-.005 3.776-.003 3.833h3.645zm5.634 4.134a2.061 2.061 0 0 0-1.969 1.336 1.963 1.963 0 0 1 2.343-.739c.396.161.917.422 1.33.283a2.1 2.1 0 0 0-1.704-.88zm3.39 1.061c-.375-.13-.8-.277-1.109-.681-.06-.08-.116-.17-.176-.265a2.143 2.143 0 0 0-.533-.642c-.294-.216-.68-.322-1.18-.322a2.482 2.482 0 0 0-2.294 1.536 2.325 2.325 0 0 1 4.002.388.75.75 0 0 0 .836.334c.493-.105.46.36 1.203.518v-.133c-.003-.446-.246-.55-.75-.733zm2.024 1.266a.723.723 0 0 0 .347-.638c-.01-2.957-2.41-5.487-5.37-5.487a5.364 5.364 0 0 0-4.487 2.418c-.01-.026-1.522-2.39-1.538-2.418H8.943l3.463 5.423-3.379 5.32h3.54l1.54-2.366 1.568 2.366h3.541l-3.21-5.052a.7.7 0 0 1-.084-.32 2.69 2.69 0 0 1 2.69-2.691h.001c1.488 0 1.736.89 2.057 1.308.634.826 1.9.464 1.9 1.541a.707.707 0 0 0 1.066.596zm.35.133c-.173.372-.56.338-.755.639-.176.271.114.412.114.412s.337.156.538-.311c.104-.231.14-.488.103-.74z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- MIDDLE CONTENT -->
|
||||
<div id="middle-content">
|
||||
<div id="learning-materials" class="rounded shadow">
|
||||
<h2>Learning materials</h2>
|
||||
<a href="https://nx.dev/getting-started/intro?utm_source=nx-project" target="_blank" rel="noreferrer" class="list-item-link">
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
|
||||
/>
|
||||
</svg>
|
||||
<span>
|
||||
Documentation
|
||||
<span> Everything is in there </span>
|
||||
</span>
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 5l7 7-7 7"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
<a href="https://blog.nrwl.io/?utm_source=nx-project" target="_blank" rel="noreferrer" class="list-item-link">
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"
|
||||
/>
|
||||
</svg>
|
||||
<span>
|
||||
Blog
|
||||
<span> Changelog, features & events </span>
|
||||
</span>
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 5l7 7-7 7"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
<a href="https://www.youtube.com/c/Nrwl_io/videos?utm_source=nx-project&sub_confirmation=1" target="_blank" rel="noreferrer" class="list-item-link">
|
||||
<svg
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>YouTube</title>
|
||||
<path
|
||||
d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"
|
||||
/>
|
||||
</svg>
|
||||
<span>
|
||||
YouTube channel
|
||||
<span> Nx Show, talks & tutorials </span>
|
||||
</span>
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 5l7 7-7 7"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
<a href="https://nx.dev/react-tutorial/1-code-generation?utm_source=nx-project" target="_blank" rel="noreferrer" class="list-item-link">
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M15 15l-2 5L9 9l11 4-5 2zm0 0l5 5M7.188 2.239l.777 2.897M5.136 7.965l-2.898-.777M13.95 4.05l-2.122 2.122m-5.657 5.656l-2.12 2.122"
|
||||
/>
|
||||
</svg>
|
||||
<span>
|
||||
Interactive tutorials
|
||||
<span> Create an app, step-by-step </span>
|
||||
</span>
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 5l7 7-7 7"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
<a href="https://nxplaybook.com/?utm_source=nx-project" target="_blank" rel="noreferrer" class="list-item-link">
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path d="M12 14l9-5-9-5-9 5 9 5z" />
|
||||
<path
|
||||
d="M12 14l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14z"
|
||||
/>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M12 14l9-5-9-5-9 5 9 5zm0 0l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14zm-4 6v-7.5l4-2.222"
|
||||
/>
|
||||
</svg>
|
||||
<span>
|
||||
Video courses
|
||||
<span> Nx custom courses </span>
|
||||
</span>
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M9 5l7 7-7 7"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
</div>
|
||||
<div id="other-links">
|
||||
<a id="nx-console" class="button-pill rounded shadow" href="https://marketplace.visualstudio.com/items?itemName=nrwl.angular-console&utm_source=nx-project" target="_blank" rel="noreferrer">
|
||||
<svg
|
||||
fill="currentColor"
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<title>Visual Studio Code</title>
|
||||
<path
|
||||
d="M23.15 2.587L18.21.21a1.494 1.494 0 0 0-1.705.29l-9.46 8.63-4.12-3.128a.999.999 0 0 0-1.276.057L.327 7.261A1 1 0 0 0 .326 8.74L3.899 12 .326 15.26a1 1 0 0 0 .001 1.479L1.65 17.94a.999.999 0 0 0 1.276.057l4.12-3.128 9.46 8.63a1.492 1.492 0 0 0 1.704.29l4.942-2.377A1.5 1.5 0 0 0 24 20.06V3.939a1.5 1.5 0 0 0-.85-1.352zm-5.146 14.861L10.826 12l7.178-5.448v10.896z"
|
||||
/>
|
||||
</svg>
|
||||
<span>
|
||||
Install Nx Console
|
||||
<span>Plugin for VSCode</span>
|
||||
</span>
|
||||
</a>
|
||||
<div id="nx-cloud" class="rounded shadow">
|
||||
<div>
|
||||
<svg
|
||||
viewBox="0 0 120 120"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M120 15V30C103.44 30 90 43.44 90 60C90 76.56 76.56 90 60 90C43.44 90 30 103.44 30 120H15C6.72 120 0 113.28 0 105V15C0 6.72 6.72 0 15 0H105C113.28 0 120 6.72 120 15Z"
|
||||
fill="#0E2039"
|
||||
/>
|
||||
<path
|
||||
d="M120 30V105C120 113.28 113.28 120 105 120H30C30 103.44 43.44 90 60 90C76.56 90 90 76.56 90 60C90 43.44 103.44 30 120 30Z"
|
||||
fill="white"
|
||||
/>
|
||||
</svg>
|
||||
<h2>
|
||||
NxCloud
|
||||
<span>
|
||||
Enable faster CI & better DX
|
||||
</span>
|
||||
</h2>
|
||||
</div>
|
||||
<p>
|
||||
You can activate distributed tasks executions and caching by
|
||||
running:
|
||||
</p>
|
||||
<pre>nx connect-to-nx-cloud</pre>
|
||||
<a href="https://nx.app/?utm_source=nx-project" target="_blank" rel="noreferrer"> What is Nx Cloud? </a>
|
||||
</div>
|
||||
<a id="nx-repo" class="button-pill rounded shadow" href="https://github.com/nrwl/nx?utm_source=nx-project" target="_blank" rel="noreferrer">
|
||||
<svg
|
||||
fill="currentColor"
|
||||
role="img"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
|
||||
/>
|
||||
</svg>
|
||||
<span>
|
||||
Nx is open source
|
||||
<span> Love Nx? Give us a star! </span>
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- COMMANDS -->
|
||||
<div id="commands" class="rounded shadow">
|
||||
<h2>Next steps</h2>
|
||||
<p>Here are some things you can do with Nx:</p>
|
||||
<details>
|
||||
<summary>
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
Add UI library
|
||||
</summary>
|
||||
<pre><span># Generate UI lib</span>
|
||||
nx g @nrwl/angular:lib ui
|
||||
|
||||
<span># Add a component</span>
|
||||
nx g @nrwl/angular:component button --project ui</pre>
|
||||
</details>
|
||||
<details>
|
||||
<summary>
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
View interactive project graph
|
||||
</summary>
|
||||
<pre>nx graph</pre>
|
||||
</details>
|
||||
<details>
|
||||
<summary>
|
||||
<svg
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
|
||||
/>
|
||||
</svg>
|
||||
Run affected commands
|
||||
</summary>
|
||||
<pre><span># see what's been affected by changes</span>
|
||||
nx affected:graph
|
||||
|
||||
<span># run tests for current changes</span>
|
||||
nx affected:test
|
||||
|
||||
<span># run e2e tests for current changes</span>
|
||||
nx affected:e2e</pre>
|
||||
</details>
|
||||
</div>
|
||||
|
||||
<p id="love">
|
||||
Carefully crafted with
|
||||
<svg
|
||||
fill="currentColor"
|
||||
stroke="none"
|
||||
viewBox="0 0 24 24"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
|
||||
/>
|
||||
</svg>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
customElements.define('<%= prefix %>-root', AppElement);
|
||||
@ -0,0 +1,3 @@
|
||||
export const environment = {
|
||||
production: true
|
||||
};
|
||||
@ -0,0 +1,6 @@
|
||||
// This file can be replaced during build by using the `fileReplacements` array.
|
||||
// When building for production, this file is replaced with `environment.prod.ts`.
|
||||
|
||||
export const environment = {
|
||||
production: false
|
||||
};
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
@ -0,0 +1 @@
|
||||
import './app/app.element.ts';
|
||||
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* Polyfill stable language features. These imports will be optimized by `@babel/preset-env`.
|
||||
*
|
||||
* See: https://github.com/zloirock/core-js#babel
|
||||
*/
|
||||
import 'core-js/stable';
|
||||
import 'regenerator-runtime/runtime';
|
||||
@ -0,0 +1 @@
|
||||
/* You can add global styles to this file, and also import other style files */
|
||||
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "<%= offsetFromRoot %>dist/out-tsc",
|
||||
"types": ["node"]
|
||||
},
|
||||
"exclude": ["jest.config.ts","**/*.spec.ts", "**/*.test.ts"],
|
||||
"include": ["**/*.ts"]
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
{
|
||||
"extends": "<%= rootTsConfigPath %>",
|
||||
"files": [],
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"useDefineForClassFields": true,
|
||||
"module": "ESNext",
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"moduleResolution": "Node",
|
||||
"strict": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"esModuleInterop": true,
|
||||
"noEmit": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"noImplicitReturns": true,
|
||||
"skipLibCheck": true,
|
||||
"types": ["vite/client"]
|
||||
},
|
||||
"include": ["src"],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -4,7 +4,7 @@ export interface Schema {
|
||||
name: string;
|
||||
prefix?: string;
|
||||
style?: string;
|
||||
bundler?: 'webpack' | 'none';
|
||||
bundler?: 'webpack' | 'none' | 'vite';
|
||||
compiler?: 'babel' | 'swc';
|
||||
skipFormat?: boolean;
|
||||
directory?: string;
|
||||
|
||||
@ -56,8 +56,9 @@
|
||||
"bundler": {
|
||||
"type": "string",
|
||||
"description": "The bundler to use.",
|
||||
"enum": ["webpack", "none"],
|
||||
"default": "webpack"
|
||||
"enum": ["webpack", "none", "vite"],
|
||||
"default": "webpack",
|
||||
"x-prompt": "Which bundler do you want to use?"
|
||||
},
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
@ -96,5 +97,6 @@
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
"required": [],
|
||||
"examplesFile": "../../../docs/application-examples.md"
|
||||
}
|
||||
|
||||
2
packages/web/src/generators/init/schema.d.ts
vendored
2
packages/web/src/generators/init/schema.d.ts
vendored
@ -1,5 +1,5 @@
|
||||
export interface Schema {
|
||||
bundler?: 'webpack' | 'none';
|
||||
bundler?: 'webpack' | 'none' | 'vite';
|
||||
unitTestRunner?: 'jest' | 'none';
|
||||
e2eTestRunner?: 'cypress' | 'none';
|
||||
skipFormat?: boolean;
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
"bundler": {
|
||||
"type": "string",
|
||||
"description": "The bundler to use.",
|
||||
"enum": ["webpack", "none"],
|
||||
"enum": ["webpack", "none", "vite"],
|
||||
"default": "webpack"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
|
||||
@ -94,6 +94,8 @@
|
||||
"@nrwl/tao": ["packages/tao"],
|
||||
"@nrwl/tao/*": ["packages/tao/*"],
|
||||
"@nrwl/typedoc-theme": ["/typedoc-theme/src/index.ts"],
|
||||
"@nrwl/vite": ["packages/vite"],
|
||||
"@nrwl/vite/*": ["packages/vite/*"],
|
||||
"@nrwl/web": ["packages/web"],
|
||||
"@nrwl/web/*": ["packages/web/*"],
|
||||
"@nrwl/webpack": ["packages/webpack"],
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user