feat(react): update app and lib generators to support new TS solution setup (#28808)
This PR updates app and lib generators in the following packages such that they will generate files with the TS solution setup if it is detected. - `@nx/react` - `@nx/next` - `@nx/remix` - `@nx/expo` - `@nx/react-native` React apps and libs will be linked using npm/pnpm/yarn/bun workspaces feature rather than through tsconfig paths. This means that local aliases like `@/` will work with Next.js and Remix apps. Note: This will be behind `--workspaces` flag when using `npx create-nx-workspace` and choosing React stack. If you use the None/TS stack then adding plugins like `nx add @nx/react` then generating apps, it will automatically pick up the new TS solution setup. <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior React generators are not compatible with TS solution setup (i.e. workspaces + TS project references). ## Expected Behavior React generators work with new TS solution setup (Plain, Next.js, Remix, Expo, React Native). ## Related Issue(s) #28322 --------- Co-authored-by: Leosvel Pérez Espinosa <leosvel.perez.espinosa@gmail.com> Co-authored-by: Nicholas Cunningham <ndcunningham@gmail.com>
This commit is contained in:
parent
2cb58b937d
commit
ec5a5e6360
@ -28,6 +28,7 @@ Install `create-nx-workspace` globally to invoke the command directly, or use `n
|
||||
| `--defaultBase` | string | Default base to use for new projects. (Default: `main`) |
|
||||
| `--docker` | boolean | Generate a Dockerfile for the Node API. |
|
||||
| `--e2eTestRunner` | `playwright`, `cypress`, `none` | Test runner to use for end to end (E2E) tests. |
|
||||
| `--formatter` | string | Code formatter to use. |
|
||||
| `--framework` | string | Framework option to be used with certain stacks. |
|
||||
| `--help` | boolean | Show help. |
|
||||
| `--interactive` | boolean | Enable interactive mode with presets. (Default: `true`) |
|
||||
@ -45,6 +46,7 @@ Install `create-nx-workspace` globally to invoke the command directly, or use `n
|
||||
| `--style` | string | Stylesheet type to be used with certain stacks. |
|
||||
| `--useGitHub` | boolean | Will you be using GitHub as your git hosting provider? (Default: `false`) |
|
||||
| `--version` | boolean | Show version number. |
|
||||
| `--workspaces` | boolean | Use package manager workspaces. (Default: `false`) |
|
||||
| `--workspaceType` | `integrated`, `package-based`, `standalone` | The type of workspace to create. |
|
||||
|
||||
## Presets
|
||||
|
||||
@ -44,13 +44,15 @@
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "eslint"
|
||||
"default": "none",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"description": "Test runner to use for unit tests",
|
||||
"default": "jest"
|
||||
"default": "none",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"tags": {
|
||||
"type": "string",
|
||||
@ -71,7 +73,8 @@
|
||||
"description": "Adds the specified e2e test runner",
|
||||
"type": "string",
|
||||
"enum": ["playwright", "cypress", "detox", "none"],
|
||||
"default": "none"
|
||||
"default": "none",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"standaloneConfig": {
|
||||
"description": "Split the project configuration into `<projectRoot>/project.json` rather than including it inside `workspace.json`.",
|
||||
|
||||
@ -29,13 +29,16 @@
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "eslint"
|
||||
"default": "none",
|
||||
"x-prompt": "Which linter would you like to use?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"description": "Test runner to use for unit tests.",
|
||||
"default": "jest"
|
||||
"default": "none",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"tags": {
|
||||
"type": "string",
|
||||
|
||||
@ -63,8 +63,10 @@
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint"],
|
||||
"default": "eslint"
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "none",
|
||||
"x-prompt": "Which linter would you like to use?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
@ -76,7 +78,9 @@
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"description": "Test runner to use for unit tests.",
|
||||
"default": "jest"
|
||||
"default": "none",
|
||||
"x-prompt": "What unit test runner should be used?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"e2eTestRunner": {
|
||||
"type": "string",
|
||||
|
||||
@ -56,17 +56,29 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"bundler": {
|
||||
"type": "string",
|
||||
"description": "The bundler to use. Choosing 'none' means this library is not buildable.",
|
||||
"enum": ["none", "vite", "rollup"],
|
||||
"default": "none",
|
||||
"x-prompt": "Which bundler would you like to use to build the library? Choose 'none' to skip build setup.",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint"],
|
||||
"default": "eslint"
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "none",
|
||||
"x-prompt": "Which linter would you like to use?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["vitest", "jest", "none"],
|
||||
"description": "Test runner to use for unit tests.",
|
||||
"default": "vitest"
|
||||
"default": "none",
|
||||
"x-prompt": "What unit test runner should be used?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"tags": {
|
||||
"type": "string",
|
||||
@ -99,7 +111,8 @@
|
||||
"buildable": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Generate a buildable library."
|
||||
"description": "Generate a buildable library that uses rollup to bundle.",
|
||||
"x-deprecated": "Use the `bundler` option for greater control (none, vite, rollup)."
|
||||
},
|
||||
"importPath": {
|
||||
"type": "string",
|
||||
|
||||
@ -28,6 +28,7 @@ Install `create-nx-workspace` globally to invoke the command directly, or use `n
|
||||
| `--defaultBase` | string | Default base to use for new projects. (Default: `main`) |
|
||||
| `--docker` | boolean | Generate a Dockerfile for the Node API. |
|
||||
| `--e2eTestRunner` | `playwright`, `cypress`, `none` | Test runner to use for end to end (E2E) tests. |
|
||||
| `--formatter` | string | Code formatter to use. |
|
||||
| `--framework` | string | Framework option to be used with certain stacks. |
|
||||
| `--help` | boolean | Show help. |
|
||||
| `--interactive` | boolean | Enable interactive mode with presets. (Default: `true`) |
|
||||
@ -45,6 +46,7 @@ Install `create-nx-workspace` globally to invoke the command directly, or use `n
|
||||
| `--style` | string | Stylesheet type to be used with certain stacks. |
|
||||
| `--useGitHub` | boolean | Will you be using GitHub as your git hosting provider? (Default: `false`) |
|
||||
| `--version` | boolean | Show version number. |
|
||||
| `--workspaces` | boolean | Use package manager workspaces. (Default: `false`) |
|
||||
| `--workspaceType` | `integrated`, `package-based`, `standalone` | The type of workspace to create. |
|
||||
|
||||
## Presets
|
||||
|
||||
@ -44,13 +44,15 @@
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "eslint"
|
||||
"default": "none",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"description": "Test runner to use for unit tests",
|
||||
"default": "jest"
|
||||
"default": "none",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"tags": {
|
||||
"type": "string",
|
||||
@ -71,7 +73,8 @@
|
||||
"description": "Adds the specified e2e test runner.",
|
||||
"type": "string",
|
||||
"enum": ["playwright", "cypress", "detox", "none"],
|
||||
"default": "playwright"
|
||||
"default": "none",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"install": {
|
||||
"type": "boolean",
|
||||
|
||||
@ -32,13 +32,16 @@
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "eslint"
|
||||
"default": "none",
|
||||
"x-prompt": "Which linter would you like to use?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"description": "Test runner to use for unit tests.",
|
||||
"default": "jest"
|
||||
"default": "none",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"tags": {
|
||||
"type": "string",
|
||||
|
||||
@ -74,12 +74,6 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "eslint"
|
||||
},
|
||||
"routing": {
|
||||
"type": "boolean",
|
||||
"description": "Generate application with routes.",
|
||||
@ -98,11 +92,29 @@
|
||||
"default": false,
|
||||
"x-priority": "internal"
|
||||
},
|
||||
"bundler": {
|
||||
"description": "The bundler to use.",
|
||||
"type": "string",
|
||||
"enum": ["vite", "webpack", "rspack"],
|
||||
"x-prompt": "Which bundler do you want to use to build the application?",
|
||||
"default": "vite",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "none",
|
||||
"x-prompt": "Which linter would you like to use?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["vitest", "jest", "none"],
|
||||
"description": "Test runner to use for unit tests.",
|
||||
"default": "vitest"
|
||||
"default": "none",
|
||||
"x-prompt": "What unit test runner should be used?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"inSourceTests": {
|
||||
"type": "boolean",
|
||||
@ -165,14 +177,6 @@
|
||||
"default": false,
|
||||
"hidden": true
|
||||
},
|
||||
"bundler": {
|
||||
"description": "The bundler to use.",
|
||||
"type": "string",
|
||||
"enum": ["vite", "webpack", "rspack"],
|
||||
"x-prompt": "Which bundler do you want to use to build the application?",
|
||||
"default": "vite",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"minimal": {
|
||||
"description": "Generate a React app with a minimal setup, no separate test files.",
|
||||
"type": "boolean",
|
||||
|
||||
@ -65,18 +65,29 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"bundler": {
|
||||
"type": "string",
|
||||
"description": "The bundler to use. Choosing 'none' means this library is not buildable.",
|
||||
"enum": ["none", "vite", "rollup"],
|
||||
"default": "none",
|
||||
"x-prompt": "Which bundler would you like to use to build the library? Choose 'none' to skip build setup.",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "eslint"
|
||||
"default": "none",
|
||||
"x-prompt": "Which linter would you like to use?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["vitest", "jest", "none"],
|
||||
"default": "vitest",
|
||||
"default": "none",
|
||||
"description": "Test runner to use for unit tests.",
|
||||
"x-prompt": "What unit test runner should be used?"
|
||||
"x-prompt": "What unit test runner should be used?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"inSourceTests": {
|
||||
"type": "boolean",
|
||||
@ -148,14 +159,6 @@
|
||||
"description": "Whether or not to configure the ESLint `parserOptions.project` option. We do not do this by default for lint performance reasons.",
|
||||
"default": false
|
||||
},
|
||||
"bundler": {
|
||||
"type": "string",
|
||||
"description": "The bundler to use. Choosing 'none' means this library is not buildable.",
|
||||
"enum": ["none", "vite", "rollup"],
|
||||
"default": "none",
|
||||
"x-prompt": "Which bundler would you like to use to build the library? Choose 'none' to skip build setup.",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"compiler": {
|
||||
"type": "string",
|
||||
"enum": ["babel", "swc"],
|
||||
|
||||
@ -24,19 +24,22 @@
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "eslint"
|
||||
"default": "none",
|
||||
"x-prompt": "Which linter would you like to use?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["vitest", "jest", "none"],
|
||||
"default": "vitest",
|
||||
"default": "none",
|
||||
"description": "Test runner to use for unit tests.",
|
||||
"x-prompt": "What unit test runner should be used?"
|
||||
"x-prompt": "What unit test runner should be used?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"e2eTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["playwright", "cypress", "none"],
|
||||
"default": "playwright",
|
||||
"default": "none",
|
||||
"description": "Test runner to use for e2e tests",
|
||||
"x-prompt": "Which E2E test runner would you like to use?"
|
||||
},
|
||||
|
||||
@ -37,17 +37,29 @@
|
||||
"enum": ["none", "css"],
|
||||
"default": "css"
|
||||
},
|
||||
"buildable": {
|
||||
"type": "boolean",
|
||||
"description": "Should the library be buildable?",
|
||||
"default": false
|
||||
"bundler": {
|
||||
"type": "string",
|
||||
"description": "The bundler to use. Choosing 'none' means this library is not buildable.",
|
||||
"enum": ["none", "vite", "rollup"],
|
||||
"default": "none",
|
||||
"x-prompt": "Which bundler would you like to use to build the library? Choose 'none' to skip build setup.",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "none",
|
||||
"x-prompt": "Which linter would you like to use?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["vitest", "jest", "none"],
|
||||
"description": "Test Runner to use for Unit Tests",
|
||||
"x-prompt": "What test runner should be used?",
|
||||
"default": "vitest"
|
||||
"default": "none",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"importPath": {
|
||||
"type": "string",
|
||||
@ -63,6 +75,12 @@
|
||||
"description": "Skip formatting files after generator runs",
|
||||
"default": false,
|
||||
"x-priority": "internal"
|
||||
},
|
||||
"buildable": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Generate a buildable library that uses rollup to bundle.",
|
||||
"x-deprecated": "Use the `bundler` option for greater control (none, vite, rollup)."
|
||||
}
|
||||
},
|
||||
"required": ["directory"],
|
||||
|
||||
@ -119,7 +119,7 @@
|
||||
"extractLicenses": {
|
||||
"type": "boolean",
|
||||
"description": "Extract all licenses in a separate file.",
|
||||
"default": true
|
||||
"default": false
|
||||
},
|
||||
"fileReplacements": {
|
||||
"description": "Replace files with other files in the build.",
|
||||
|
||||
@ -47,7 +47,7 @@
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint"],
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "eslint"
|
||||
},
|
||||
"packageManager": {
|
||||
@ -89,6 +89,11 @@
|
||||
"type": "string",
|
||||
"enum": ["none", "prettier"],
|
||||
"default": "none"
|
||||
},
|
||||
"workspaces": {
|
||||
"description": "Whether to use package manager workspaces.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"additionalProperties": true,
|
||||
|
||||
@ -17,7 +17,7 @@
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint"],
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "eslint"
|
||||
},
|
||||
"routing": {
|
||||
@ -100,6 +100,17 @@
|
||||
"prefix": {
|
||||
"description": "The prefix to use for Angular component and directive selectors.",
|
||||
"type": "string"
|
||||
},
|
||||
"formatter": {
|
||||
"description": "The tool to use for code formatting.",
|
||||
"type": "string",
|
||||
"enum": ["none", "prettier"],
|
||||
"default": "none"
|
||||
},
|
||||
"workspaces": {
|
||||
"description": "Whether to use package manager workspaces.",
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"required": ["preset", "name"],
|
||||
|
||||
@ -27,10 +27,13 @@ describe('Linter (legacy)', () => {
|
||||
newProject({
|
||||
packages: ['@nx/react', '@nx/js', '@nx/eslint'],
|
||||
});
|
||||
runCLI(`generate @nx/react:app apps/${myapp} --tags=validtag`, {
|
||||
runCLI(
|
||||
`generate @nx/react:app apps/${myapp} --tags=validtag --linter=eslint`,
|
||||
{
|
||||
env: { NX_ADD_PLUGINS: 'false' },
|
||||
});
|
||||
runCLI(`generate @nx/js:lib apps/${mylib}`, {
|
||||
}
|
||||
);
|
||||
runCLI(`generate @nx/js:lib apps/${mylib} --linter=eslint`, {
|
||||
env: { NX_ADD_PLUGINS: 'false' },
|
||||
});
|
||||
});
|
||||
@ -135,10 +138,10 @@ describe('Linter (legacy)', () => {
|
||||
bundler: 'vite',
|
||||
e2eTestRunner: 'none',
|
||||
});
|
||||
runCLI(`generate @nx/js:lib libs/${mylib}`, {
|
||||
runCLI(`generate @nx/js:lib libs/${mylib} --linter=eslint`, {
|
||||
env: { NX_ADD_PLUGINS: 'false' },
|
||||
});
|
||||
runCLI(`generate @nx/js:lib libs/${mylib2}`, {
|
||||
runCLI(`generate @nx/js:lib libs/${mylib2} --linter=eslint`, {
|
||||
env: { NX_ADD_PLUGINS: 'false' },
|
||||
});
|
||||
|
||||
@ -190,7 +193,7 @@ describe('Linter (legacy)', () => {
|
||||
bundler: 'vite',
|
||||
e2eTestRunner: 'none',
|
||||
});
|
||||
runCLI(`generate @nx/js:lib ${mylib}`, {
|
||||
runCLI(`generate @nx/js:lib ${mylib} --linter=eslint`, {
|
||||
env: { NX_ADD_PLUGINS: 'false' },
|
||||
});
|
||||
|
||||
|
||||
@ -35,8 +35,10 @@ describe('Linter', () => {
|
||||
projScope = newProject({
|
||||
packages: ['@nx/react', '@nx/js', '@nx/eslint'],
|
||||
});
|
||||
runCLI(`generate @nx/react:app apps/${myapp} --tags=validtag`);
|
||||
runCLI(`generate @nx/js:lib libs/${mylib}`);
|
||||
runCLI(
|
||||
`generate @nx/react:app apps/${myapp} --tags=validtag --linter eslint --unitTestRunner vitest`
|
||||
);
|
||||
runCLI(`generate @nx/js:lib libs/${mylib} --linter eslint`);
|
||||
});
|
||||
afterAll(() => cleanupProject());
|
||||
|
||||
@ -218,10 +220,14 @@ describe('Linter', () => {
|
||||
const invalidtaglib = uniq('invalidtaglib');
|
||||
const validtaglib = uniq('validtaglib');
|
||||
|
||||
runCLI(`generate @nx/react:app apps/${myapp2}`);
|
||||
runCLI(`generate @nx/react:lib libs/${lazylib}`);
|
||||
runCLI(`generate @nx/js:lib libs/${invalidtaglib} --tags=invalidtag`);
|
||||
runCLI(`generate @nx/js:lib libs/${validtaglib} --tags=validtag`);
|
||||
runCLI(`generate @nx/react:app apps/${myapp2} --linter eslint`);
|
||||
runCLI(`generate @nx/react:lib libs/${lazylib} --linter eslint`);
|
||||
runCLI(
|
||||
`generate @nx/js:lib libs/${invalidtaglib} --linter eslint --tags=invalidtag`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/js:lib libs/${validtaglib} --linter eslint --tags=validtag`
|
||||
);
|
||||
|
||||
const eslint = readJson('.eslintrc.json');
|
||||
eslint.overrides[0].rules[
|
||||
@ -283,9 +289,15 @@ describe('Linter', () => {
|
||||
|
||||
beforeAll(() => {
|
||||
// make these libs non-buildable to avoid dep-checks triggering lint errors
|
||||
runCLI(`generate @nx/js:lib libs/${libA} --bundler=none`);
|
||||
runCLI(`generate @nx/js:lib libs/${libB} --bundler=none`);
|
||||
runCLI(`generate @nx/js:lib libs/${libC} --bundler=none`);
|
||||
runCLI(
|
||||
`generate @nx/js:lib libs/${libA} --bundler=none --linter eslint`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/js:lib libs/${libB} --bundler=none --linter eslint`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/js:lib libs/${libC} --bundler=none --linter eslint`
|
||||
);
|
||||
|
||||
/**
|
||||
* create tslib-a structure
|
||||
@ -599,8 +611,8 @@ describe('Linter', () => {
|
||||
const reactLib = uniq('react-lib');
|
||||
const jsLib = uniq('js-lib');
|
||||
|
||||
runCLI(`generate @nx/react:lib ${reactLib}`);
|
||||
runCLI(`generate @nx/js:lib ${jsLib}`);
|
||||
runCLI(`generate @nx/react:lib ${reactLib} --linter eslint`);
|
||||
runCLI(`generate @nx/js:lib ${jsLib} --linter eslint`);
|
||||
|
||||
checkFilesExist(
|
||||
`${reactLib}/eslint.config.js`,
|
||||
@ -687,7 +699,7 @@ describe('Linter', () => {
|
||||
const mylib = uniq('mylib');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/react:app --name=${myapp} --unitTestRunner=jest --directory="."`
|
||||
`generate @nx/react:app --name=${myapp} --unitTestRunner=jest --linter eslint --directory="."`
|
||||
);
|
||||
verifySuccessfulStandaloneSetup(myapp);
|
||||
|
||||
@ -701,7 +713,9 @@ describe('Linter', () => {
|
||||
let e2eOverrides = JSON.stringify(e2eEslint.overrides);
|
||||
expect(e2eOverrides).toContain('plugin:@nx/javascript');
|
||||
|
||||
runCLI(`generate @nx/js:lib libs/${mylib} --unitTestRunner=jest`);
|
||||
runCLI(
|
||||
`generate @nx/js:lib libs/${mylib} --unitTestRunner=jest --linter eslint`
|
||||
);
|
||||
verifySuccessfulMigratedSetup(myapp, mylib);
|
||||
|
||||
appEslint = readJson(`.eslintrc.json`);
|
||||
@ -721,7 +735,7 @@ describe('Linter', () => {
|
||||
const mylib = uniq('mylib');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/angular:app --name=${myapp} --directory="." --no-interactive`
|
||||
`generate @nx/angular:app --name=${myapp} --directory="." --linter eslint --no-interactive`
|
||||
);
|
||||
verifySuccessfulStandaloneSetup(myapp);
|
||||
|
||||
@ -734,7 +748,9 @@ describe('Linter', () => {
|
||||
let e2eOverrides = JSON.stringify(e2eEslint.overrides);
|
||||
expect(e2eOverrides).toContain('plugin:@nx/javascript');
|
||||
|
||||
runCLI(`generate @nx/js:lib libs/${mylib} --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nx/js:lib libs/${mylib} --linter eslint --no-interactive`
|
||||
);
|
||||
verifySuccessfulMigratedSetup(myapp, mylib);
|
||||
|
||||
appEslint = readJson(`.eslintrc.json`);
|
||||
@ -752,7 +768,7 @@ describe('Linter', () => {
|
||||
const mylib = uniq('mylib');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/node:app --name=${myapp} --directory="." --no-interactive`
|
||||
`generate @nx/node:app --name=${myapp} --linter eslint --directory="." --no-interactive`
|
||||
);
|
||||
verifySuccessfulStandaloneSetup(myapp);
|
||||
|
||||
@ -767,7 +783,9 @@ describe('Linter', () => {
|
||||
expect(e2eOverrides).toContain('plugin:@nx/javascript');
|
||||
expect(e2eOverrides).toContain('plugin:@nx/typescript');
|
||||
|
||||
runCLI(`generate @nx/js:lib libs/${mylib} --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nx/js:lib libs/${mylib} --linter eslint --no-interactive`
|
||||
);
|
||||
verifySuccessfulMigratedSetup(myapp, mylib);
|
||||
|
||||
appEslint = readJson(`.eslintrc.json`);
|
||||
|
||||
@ -40,10 +40,10 @@ describe('@nx/expo (legacy)', () => {
|
||||
return nxJson;
|
||||
});
|
||||
runCLI(
|
||||
`generate @nx/expo:application apps/${appName} --e2eTestRunner=cypress --no-interactive`
|
||||
`generate @nx/expo:application apps/${appName} --e2eTestRunner=cypress --no-interactive --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/expo:library libs/${libName} --buildable --publishable --importPath=${proj}/${libName}`
|
||||
`generate @nx/expo:library libs/${libName} --buildable --publishable --importPath=${proj}/${libName} --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
});
|
||||
afterAll(() => {
|
||||
@ -210,7 +210,9 @@ describe('@nx/expo (legacy)', () => {
|
||||
const appName = uniq('app1');
|
||||
const libName = uniq('@my-org/lib1');
|
||||
|
||||
runCLI(`generate @nx/expo:application ${appName} --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nx/expo:application ${appName} --no-interactive --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
|
||||
// check files are generated without the layout directory ("apps/") and
|
||||
// using the project name as the directory when no directory is provided
|
||||
@ -221,7 +223,9 @@ describe('@nx/expo (legacy)', () => {
|
||||
`Successfully ran target test for project ${appName}`
|
||||
);
|
||||
|
||||
runCLI(`generate @nx/expo:library ${libName} --buildable`);
|
||||
runCLI(
|
||||
`generate @nx/expo:library ${libName} --buildable --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
|
||||
// check files are generated without the layout directory ("libs/") and
|
||||
// using the project name as the directory when no directory is provided
|
||||
@ -274,7 +278,7 @@ describe('@nx/expo (legacy)', () => {
|
||||
it('should run e2e for playwright', async () => {
|
||||
const appName2 = uniq('my-app');
|
||||
runCLI(
|
||||
`generate @nx/expo:application ${appName2} --e2eTestRunner=playwright --no-interactive`
|
||||
`generate @nx/expo:application ${appName2} --e2eTestRunner=playwright --no-interactive --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
if (runE2ETests()) {
|
||||
const results = runCLI(`e2e ${appName2}-e2e`, { verbose: true });
|
||||
|
||||
@ -23,7 +23,9 @@ describe('@nx/expo', () => {
|
||||
beforeAll(() => {
|
||||
newProject();
|
||||
appName = uniq('app');
|
||||
runCLI(`generate @nx/expo:app ${appName} --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nx/expo:app ${appName} --no-interactive --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
});
|
||||
|
||||
afterAll(() => cleanupProject());
|
||||
@ -152,7 +154,7 @@ describe('@nx/expo', () => {
|
||||
|
||||
it('should create storybook with application', async () => {
|
||||
runCLI(
|
||||
`generate @nx/react:storybook-configuration ${appName} --generateStories --no-interactive`
|
||||
`generate @nx/react:storybook-configuration ${appName} --generateStories --no-interactive --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
checkFilesExist(
|
||||
`${appName}/.storybook/main.ts`,
|
||||
|
||||
@ -10,7 +10,7 @@ describe('Jest root projects', () => {
|
||||
packages: ['@nx/angular'],
|
||||
});
|
||||
runCLI(
|
||||
`generate @nx/angular:app --name=${myapp} --directory . --rootProject --no-interactive`
|
||||
`generate @nx/angular:app --name=${myapp} --directory . --rootProject --no-interactive --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
});
|
||||
|
||||
@ -19,7 +19,9 @@ describe('Jest root projects', () => {
|
||||
}, 300_000);
|
||||
|
||||
it('should add lib project and tests should still work', async () => {
|
||||
runCLI(`generate @nx/angular:lib ${mylib} --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nx/angular:lib ${mylib} --no-interactive --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
|
||||
expect(() => runCLI(`test ${mylib}`)).not.toThrow();
|
||||
expect(() => runCLI(`test ${myapp}`)).not.toThrow();
|
||||
@ -32,7 +34,7 @@ describe('Jest root projects', () => {
|
||||
packages: ['@nx/react'],
|
||||
});
|
||||
runCLI(
|
||||
`generate @nx/react:app --name=${myapp} --directory . --rootProject`
|
||||
`generate @nx/react:app --name=${myapp} --directory . --rootProject --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
});
|
||||
|
||||
@ -41,7 +43,9 @@ describe('Jest root projects', () => {
|
||||
}, 300_000);
|
||||
|
||||
it('should add lib project and tests should still work', async () => {
|
||||
runCLI(`generate @nx/react:lib ${mylib} --unitTestRunner=jest`);
|
||||
runCLI(
|
||||
`generate @nx/react:lib ${mylib} --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
|
||||
expect(() => runCLI(`test ${mylib}`)).not.toThrow();
|
||||
expect(() => runCLI(`test ${myapp}`)).not.toThrow();
|
||||
|
||||
@ -32,7 +32,7 @@ describe('Next.js Styles', () => {
|
||||
const lessApp = uniq('app');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/next:app ${lessApp} --no-interactive --style=less --appDir=false --src=false`
|
||||
`generate @nx/next:app ${lessApp} --no-interactive --style=less --appDir=false --src=false --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
|
||||
await checkApp(lessApp, {
|
||||
@ -44,7 +44,7 @@ describe('Next.js Styles', () => {
|
||||
const scApp = uniq('app');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/next:app ${scApp} --no-interactive --style=styled-components --appDir=false`
|
||||
`generate @nx/next:app ${scApp} --no-interactive --style=styled-components --appDir=false --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
|
||||
await checkApp(scApp, {
|
||||
@ -56,7 +56,7 @@ describe('Next.js Styles', () => {
|
||||
const scAppWithAppRouter = uniq('app');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/next:app ${scAppWithAppRouter} --no-interactive --style=styled-components --appDir=true`
|
||||
`generate @nx/next:app ${scAppWithAppRouter} --no-interactive --style=styled-components --appDir=true --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
|
||||
await checkApp(scAppWithAppRouter, {
|
||||
@ -68,7 +68,7 @@ describe('Next.js Styles', () => {
|
||||
const emotionApp = uniq('app');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/next:app ${emotionApp} --no-interactive --style=@emotion/styled --appDir=false`
|
||||
`generate @nx/next:app ${emotionApp} --no-interactive --style=@emotion/styled --appDir=false --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
|
||||
await checkApp(emotionApp, {
|
||||
@ -83,7 +83,7 @@ describe('Next.js Styles', () => {
|
||||
const tailwindApp = uniq('app');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/next:app ${tailwindApp} --no-interactive --style=tailwind --appDir=false --src=false`
|
||||
`generate @nx/next:app ${tailwindApp} --no-interactive --style=tailwind --appDir=false --src=false --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
|
||||
await checkApp(tailwindApp, {
|
||||
@ -107,7 +107,7 @@ describe('Next.js Styles', () => {
|
||||
const tailwindApp = uniq('app');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/next:app ${tailwindApp} --no-interactive --style=tailwind --appDir=true --src=false`
|
||||
`generate @nx/next:app ${tailwindApp} --no-interactive --style=tailwind --appDir=true --src=false --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
|
||||
await checkApp(tailwindApp, {
|
||||
|
||||
@ -37,7 +37,9 @@ describe('Next.js Applications', () => {
|
||||
const appName = uniq('app1');
|
||||
const libName = uniq('@my-org/lib1');
|
||||
|
||||
runCLI(`generate @nx/next:app ${appName} --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nx/next:app ${appName} --no-interactive --linter=eslint --unitTestRunner=jest`
|
||||
);
|
||||
|
||||
// check files are generated without the layout directory ("apps/") and
|
||||
// using the project name as the directory when no directory is provided
|
||||
@ -52,7 +54,9 @@ describe('Next.js Applications', () => {
|
||||
`Successfully ran target test for project ${appName}`
|
||||
);
|
||||
|
||||
runCLI(`generate @nx/next:lib ${libName} --buildable --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nx/next:lib ${libName} --buildable --no-interactive --linter=eslint --unitTestRunner=jest`
|
||||
);
|
||||
|
||||
// check files are generated without the layout directory ("libs/") and
|
||||
// using the project name as the directory when no directory is provided
|
||||
@ -67,7 +71,7 @@ describe('Next.js Applications', () => {
|
||||
const appName = uniq('app');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/next:app ${appName} --no-interactive --style=css --appDir=false`
|
||||
`generate @nx/next:app ${appName} --no-interactive --style=css --appDir=false --linter=eslint --unitTestRunner=jest`
|
||||
);
|
||||
|
||||
checkFilesDoNotExist(`${appName}/.next/build-manifest.json`);
|
||||
@ -82,7 +86,7 @@ describe('Next.js Applications', () => {
|
||||
const appName = uniq('app');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/next:app ${appName} --no-interactive --js --appDir=false --e2eTestRunner=playwright`
|
||||
`generate @nx/next:app ${appName} --no-interactive --js --appDir=false --e2eTestRunner=playwright --linter=eslint --unitTestRunner=jest`
|
||||
);
|
||||
|
||||
checkFilesExist(`${appName}/src/pages/index.js`);
|
||||
@ -97,7 +101,7 @@ describe('Next.js Applications', () => {
|
||||
const libName = uniq('lib');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/next:lib ${libName} --no-interactive --style=none --js`
|
||||
`generate @nx/next:lib ${libName} --no-interactive --style=none --js --linter=eslint --unitTestRunner=jest`
|
||||
);
|
||||
|
||||
const mainPath = `${appName}/src/pages/index.js`;
|
||||
@ -133,7 +137,9 @@ describe('Next.js Applications', () => {
|
||||
it('should support --no-swc flag', async () => {
|
||||
const appName = uniq('app');
|
||||
|
||||
runCLI(`generate @nx/next:app ${appName} --no-interactive --no-swc`);
|
||||
runCLI(
|
||||
`generate @nx/next:app ${appName} --no-interactive --no-swc --linter=eslint --unitTestRunner=jest`
|
||||
);
|
||||
|
||||
// Next.js enables SWC when custom .babelrc is not provided.
|
||||
checkFilesExist(`${appName}/.babelrc`);
|
||||
@ -148,7 +154,9 @@ describe('Next.js Applications', () => {
|
||||
it('should support --custom-server flag (swc)', async () => {
|
||||
const appName = uniq('app');
|
||||
|
||||
runCLI(`generate @nx/next:app ${appName} --no-interactive --custom-server`);
|
||||
runCLI(
|
||||
`generate @nx/next:app ${appName} --no-interactive --custom-server --linter=eslint --unitTestRunner=jest`
|
||||
);
|
||||
|
||||
checkFilesExist(`${appName}/server/main.ts`);
|
||||
|
||||
@ -165,7 +173,7 @@ describe('Next.js Applications', () => {
|
||||
const appName = uniq('app');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/next:app ${appName} --swc=false --no-interactive --custom-server`
|
||||
`generate @nx/next:app ${appName} --swc=false --no-interactive --custom-server --linter=eslint --unitTestRunner=jest`
|
||||
);
|
||||
|
||||
checkFilesExist(`${appName}/server/main.ts`);
|
||||
@ -182,7 +190,9 @@ describe('Next.js Applications', () => {
|
||||
it('should run e2e-ci test', async () => {
|
||||
const appName = uniq('app');
|
||||
|
||||
runCLI(`generate @nx/next:app ${appName} --no-interactive --style=css`);
|
||||
runCLI(
|
||||
`generate @nx/next:app ${appName} --no-interactive --style=css --linter=eslint --unitTestRunner=jest`
|
||||
);
|
||||
|
||||
if (runE2ETests('playwright')) {
|
||||
const e2eResults = runCLI(`e2e-ci ${appName}-e2e --verbose`, {
|
||||
@ -202,9 +212,11 @@ describe('Next.js Applications', () => {
|
||||
const appName = uniq('app');
|
||||
const pagesAppName = uniq('pages-app');
|
||||
|
||||
runCLI(`generate @nx/next:app ${appName} --style=css --no-interactive`);
|
||||
runCLI(
|
||||
`generate @nx/next:app ${pagesAppName} --appDir=false --style=css --no-interactive`
|
||||
`generate @nx/next:app ${appName} --style=css --no-interactive --linter=eslint --unitTestRunner=jest`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/next:app ${pagesAppName} --appDir=false --style=css --no-interactive --linter=eslint --unitTestRunner=jest`
|
||||
);
|
||||
|
||||
const appDirNextEnv = `${appName}/next-env.d.ts`;
|
||||
|
||||
@ -19,7 +19,7 @@ describe('@nx/workspace:convert-to-monorepo', () => {
|
||||
it('should convert a standalone webpack and jest react project to a monorepo (legacy)', async () => {
|
||||
const reactApp = uniq('reactapp');
|
||||
runCLI(
|
||||
`generate @nx/react:app --name=${reactApp} --directory="." --bundler=webpack --unitTestRunner=jest --e2eTestRunner=cypress --no-interactive`,
|
||||
`generate @nx/react:app --name=${reactApp} --directory="." --bundler=webpack --unitTestRunner=jest --e2eTestRunner=cypress --no-interactive --linter=eslint`,
|
||||
{
|
||||
env: {
|
||||
NX_ADD_PLUGINS: 'false',
|
||||
|
||||
@ -32,7 +32,7 @@ describe('@nx/workspace:infer-targets', () => {
|
||||
// default case, everything is generated with crystal, everything should be skipped
|
||||
const remixApp = uniq('remix');
|
||||
runCLI(
|
||||
`generate @nx/remix:app apps/${remixApp} --unitTestRunner jest --e2eTestRunner=playwright --no-interactive`
|
||||
`generate @nx/remix:app apps/${remixApp} --linter eslint --unitTestRunner jest --e2eTestRunner=playwright --no-interactive`
|
||||
);
|
||||
|
||||
const output = runCLI(`generate infer-targets --no-interactive --verbose`);
|
||||
@ -70,7 +70,7 @@ describe('@nx/workspace:infer-targets', () => {
|
||||
// default case, everything is generated with crystal, relevant plugins should be skipped
|
||||
const remixApp = uniq('remix');
|
||||
runCLI(
|
||||
`generate @nx/remix:app apps/${remixApp} --unitTestRunner jest --e2eTestRunner=playwright --no-interactive`
|
||||
`generate @nx/remix:app apps/${remixApp} --linter eslint --unitTestRunner jest --e2eTestRunner=playwright --no-interactive`
|
||||
);
|
||||
|
||||
const output = runCLI(
|
||||
@ -116,7 +116,7 @@ describe('@nx/workspace:infer-targets', () => {
|
||||
// even if we make sure there are executors for remix & remix-e2e, only remix conversions will run with --project option
|
||||
const remixApp = uniq('remix');
|
||||
runCLI(
|
||||
`generate @nx/remix:app apps/${remixApp} --unitTestRunner jest --e2eTestRunner=playwright --no-interactive`
|
||||
`generate @nx/remix:app apps/${remixApp} --linter eslint --unitTestRunner jest --e2eTestRunner=playwright --no-interactive`
|
||||
);
|
||||
|
||||
updateJson('nx.json', (json) => {
|
||||
@ -167,7 +167,7 @@ describe('@nx/workspace:convert-to-monorepo', () => {
|
||||
it('should be convert a standalone vite and playwright react project to a monorepo', async () => {
|
||||
const reactApp = uniq('reactapp');
|
||||
runCLI(
|
||||
`generate @nx/react:app --name=${reactApp} --directory="." --rootProject=true --bundler=vite --unitTestRunner vitest --e2eTestRunner=playwright --no-interactive`
|
||||
`generate @nx/react:app --name=${reactApp} --directory="." --rootProject=true --linter eslint --bundler=vite --unitTestRunner vitest --e2eTestRunner=playwright --no-interactive`
|
||||
);
|
||||
|
||||
runCLI('generate @nx/workspace:convert-to-monorepo --no-interactive');
|
||||
|
||||
@ -39,10 +39,10 @@ describe('@nx/react-native (legacy)', () => {
|
||||
return nxJson;
|
||||
});
|
||||
runCLI(
|
||||
`generate @nx/react-native:application ${appName} --directory=apps/${appName} --bunlder=webpack --e2eTestRunner=cypress --install=false --no-interactive`
|
||||
`generate @nx/react-native:application ${appName} --directory=apps/${appName} --bundler=webpack --e2eTestRunner=cypress --install=false --no-interactive --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/react-native:library ${libName} --directory=libs/${libName} --buildable --publishable --importPath=${proj}/${libName} --no-interactive`
|
||||
`generate @nx/react-native:library ${libName} --directory=libs/${libName} --buildable --publishable --importPath=${proj}/${libName} --no-interactive --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
});
|
||||
afterAll(() => {
|
||||
@ -265,7 +265,7 @@ describe('@nx/react-native (legacy)', () => {
|
||||
const libName = uniq('@my-org/lib1');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/react-native:application ${appName} --install=false --no-interactive`
|
||||
`generate @nx/react-native:application ${appName} --install=false --no-interactive --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
|
||||
// check files are generated without the layout directory ("apps/") and
|
||||
@ -274,7 +274,9 @@ describe('@nx/react-native (legacy)', () => {
|
||||
// check tests pass
|
||||
expect(() => runCLI(`test ${appName}`)).not.toThrow();
|
||||
|
||||
runCLI(`generate @nx/react-native:library ${libName} --buildable`);
|
||||
runCLI(
|
||||
`generate @nx/react-native:library ${libName} --buildable --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
|
||||
// check files are generated without the layout directory ("libs/") and
|
||||
// using the project name as the directory when no directory is provided
|
||||
@ -286,7 +288,7 @@ describe('@nx/react-native (legacy)', () => {
|
||||
it('should run build with vite bundler and e2e with playwright', async () => {
|
||||
const appName2 = uniq('my-app');
|
||||
runCLI(
|
||||
`generate @nx/react-native:application ${appName2} --directory=apps/${appName2} --bundler=vite --e2eTestRunner=playwright --install=false --no-interactive`
|
||||
`generate @nx/react-native:application ${appName2} --directory=apps/${appName2} --bundler=vite --e2eTestRunner=playwright --install=false --no-interactive --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
expect(() => runCLI(`build ${appName2}`)).not.toThrow();
|
||||
if (runE2ETests()) {
|
||||
|
||||
@ -18,7 +18,7 @@ describe('@nx/react-native', () => {
|
||||
newProject();
|
||||
appName = uniq('app');
|
||||
runCLI(
|
||||
`generate @nx/react-native:app ${appName} --install=false --no-interactive`
|
||||
`generate @nx/react-native:app ${appName} --install=false --no-interactive --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
});
|
||||
|
||||
@ -115,7 +115,7 @@ describe('@nx/react-native', () => {
|
||||
it('should run build with vite bundler and e2e with playwright', async () => {
|
||||
const appName2 = uniq('my-app');
|
||||
runCLI(
|
||||
`generate @nx/react-native:application ${appName2} --directory=apps/${appName2} --bundler=vite --e2eTestRunner=playwright --install=false --no-interactive`
|
||||
`generate @nx/react-native:application ${appName2} --directory=apps/${appName2} --bundler=vite --e2eTestRunner=playwright --install=false --no-interactive --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
expect(() => runCLI(`build ${appName2}`)).not.toThrow();
|
||||
if (runE2ETests()) {
|
||||
|
||||
@ -22,7 +22,7 @@ describe('Build React applications and libraries with Vite', () => {
|
||||
const viteApp = uniq('viteapp');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/react:app apps/${viteApp} --bundler=vite --compiler=babel --unitTestRunner=vitest --no-interactive`
|
||||
`generate @nx/react:app apps/${viteApp} --bundler=vite --compiler=babel --unitTestRunner=vitest --no-interactive --linter=eslint`
|
||||
);
|
||||
|
||||
const appTestResults = await runCLIAsync(`test ${viteApp}`);
|
||||
@ -43,7 +43,7 @@ describe('Build React applications and libraries with Vite', () => {
|
||||
const viteApp = uniq('viteapp');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/react:app apps/${viteApp} --bundler=vite --compiler=swc --unitTestRunner=vitest --no-interactive`
|
||||
`generate @nx/react:app apps/${viteApp} --bundler=vite --compiler=swc --unitTestRunner=vitest --no-interactive --linter=eslint`
|
||||
);
|
||||
|
||||
const appTestResults = await runCLIAsync(`test ${viteApp}`);
|
||||
@ -65,7 +65,7 @@ describe('Build React applications and libraries with Vite', () => {
|
||||
const viteLib = uniq('vitelib');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/react:app apps/${viteApp} --bundler=vite --unitTestRunner=vitest --inSourceTests --no-interactive`
|
||||
`generate @nx/react:app apps/${viteApp} --bundler=vite --unitTestRunner=vitest --inSourceTests --no-interactive --linter=eslint`
|
||||
);
|
||||
expect(() => {
|
||||
checkFilesExist(`apps/${viteApp}/src/app/app.spec.tsx`);
|
||||
@ -85,7 +85,7 @@ describe('Build React applications and libraries with Vite', () => {
|
||||
checkFilesExist(`dist/apps/${viteApp}/index.html`);
|
||||
|
||||
runCLI(
|
||||
`generate @nx/react:lib libs/${viteLib} --bundler=vite --inSourceTests --unitTestRunner=vitest --no-interactive`
|
||||
`generate @nx/react:lib libs/${viteLib} --bundler=vite --inSourceTests --unitTestRunner=vitest --no-interactive --linter=eslint`
|
||||
);
|
||||
expect(() => {
|
||||
checkFilesExist(`libs/${viteLib}/src/lib/${viteLib}.spec.tsx`);
|
||||
@ -125,7 +125,7 @@ describe('Build React applications and libraries with Vite', () => {
|
||||
const viteLib = uniq('vitelib');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/react:lib libs/${viteLib} --bundler=vite --no-interactive --unit-test-runner=none`
|
||||
`generate @nx/react:lib libs/${viteLib} --bundler=vite --no-interactive --unit-test-runner=none --linter=eslint`
|
||||
);
|
||||
|
||||
await runCLIAsync(`build ${viteLib}`);
|
||||
@ -139,7 +139,7 @@ describe('Build React applications and libraries with Vite', () => {
|
||||
// Convert non-buildable lib to buildable one
|
||||
const nonBuildableLib = uniq('nonbuildablelib');
|
||||
runCLI(
|
||||
`generate @nx/react:lib libs/${nonBuildableLib} --no-interactive --unitTestRunner=jest`
|
||||
`generate @nx/react:lib libs/${nonBuildableLib} --no-interactive --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/vite:configuration ${nonBuildableLib} --uiFramework=react --no-interactive`
|
||||
@ -157,7 +157,7 @@ describe('Build React applications and libraries with Vite', () => {
|
||||
const viteApp = uniq('viteapp');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/react:app apps/${viteApp} --bundler=vite --unitTestRunner=jest --no-interactive`
|
||||
`generate @nx/react:app apps/${viteApp} --bundler=vite --unitTestRunner=jest --no-interactive --linter=eslint`
|
||||
);
|
||||
|
||||
const appTestResults = await runCLIAsync(`test ${viteApp}`);
|
||||
|
||||
@ -34,10 +34,10 @@ describe('React Applications', () => {
|
||||
const libName = uniq('lib');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/react:app apps/${appName} --name=${appName} --bundler=vite --no-interactive --skipFormat`
|
||||
`generate @nx/react:app apps/${appName} --name=${appName} --bundler=vite --no-interactive --skipFormat --linter=eslint --unitTestRunner=vitest`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/react:lib libs/${libName} --bundler=none --no-interactive --unit-test-runner=vitest --skipFormat`
|
||||
`generate @nx/react:lib libs/${libName} --bundler=none --no-interactive --unit-test-runner=vitest --skipFormat --linter=eslint`
|
||||
);
|
||||
|
||||
// Library generated with Vite
|
||||
@ -68,10 +68,10 @@ describe('React Applications', () => {
|
||||
const libName = uniq('lib');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/react:app ${appName} --bundler=rspack --unit-test-runner=vitest --no-interactive --skipFormat`
|
||||
`generate @nx/react:app ${appName} --bundler=rspack --unit-test-runner=vitest --no-interactive --skipFormat --linter=eslint`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/react:lib ${libName} --bundler=none --no-interactive --unit-test-runner=vitest --skipFormat`
|
||||
`generate @nx/react:lib ${libName} --bundler=none --no-interactive --unit-test-runner=vitest --skipFormat --linter=eslint`
|
||||
);
|
||||
|
||||
// Library generated with Vite
|
||||
@ -109,13 +109,13 @@ describe('React Applications', () => {
|
||||
const redSvg = `<?xml version="1.0"?><svg xmlns="http://www.w3.org/2000/svg" version="1.2" baseProfile="tiny" viewBox="0 0 30 30"><rect x="10" y="10" width="10" height="10" fill="red"/></svg>`;
|
||||
|
||||
runCLI(
|
||||
`generate @nx/react:app apps/${appName} --style=css --bundler=webpack --unit-test-runner=jest --no-interactive --skipFormat`
|
||||
`generate @nx/react:app apps/${appName} --style=css --bundler=webpack --unit-test-runner=jest --no-interactive --skipFormat --linter=eslint`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/react:lib libs/${libName} --style=css --no-interactive --unit-test-runner=jest --skipFormat`
|
||||
`generate @nx/react:lib libs/${libName} --style=css --no-interactive --unit-test-runner=jest --skipFormat --linter=eslint`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/react:lib libs/${libWithNoComponents} --no-interactive --no-component --unit-test-runner=jest --skipFormat`
|
||||
`generate @nx/react:lib libs/${libWithNoComponents} --no-interactive --no-component --unit-test-runner=jest --skipFormat --linter=eslint`
|
||||
);
|
||||
|
||||
// Libs should not include package.json by default
|
||||
@ -201,7 +201,7 @@ describe('React Applications', () => {
|
||||
const appName = uniq('app');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/react:app apps/${appName} --routing --bundler=webpack --no-interactive --skipFormat`
|
||||
`generate @nx/react:app apps/${appName} --routing --bundler=webpack --no-interactive --skipFormat --linter=eslint --unitTestRunner=jest`
|
||||
);
|
||||
|
||||
runCLI(`build ${appName}`);
|
||||
@ -218,7 +218,7 @@ describe('React Applications', () => {
|
||||
const libName = uniq('lib');
|
||||
|
||||
runCLI(
|
||||
`g @nx/react:app apps/${appName} --bundler=webpack --no-interactive --skipFormat`
|
||||
`g @nx/react:app apps/${appName} --bundler=webpack --no-interactive --skipFormat --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
runCLI(
|
||||
`g @nx/react:redux apps/${appName}/src/app/lemon/lemon --skipFormat`
|
||||
@ -254,7 +254,7 @@ describe('React Applications', () => {
|
||||
const libName = uniq('@my-org/lib1');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/react:app ${appName} --bundler=webpack --no-interactive --skipFormat`
|
||||
`generate @nx/react:app ${appName} --bundler=webpack --no-interactive --skipFormat --linter=eslint --unitTestRunner=jest`
|
||||
);
|
||||
|
||||
// check files are generated without the layout directory ("apps/") and
|
||||
@ -271,7 +271,7 @@ describe('React Applications', () => {
|
||||
);
|
||||
|
||||
runCLI(
|
||||
`generate @nx/react:lib ${libName} --unit-test-runner=jest --buildable --no-interactive --skipFormat`
|
||||
`generate @nx/react:lib ${libName} --unit-test-runner=jest --buildable --no-interactive --skipFormat --linter=eslint`
|
||||
);
|
||||
|
||||
// check files are generated without the layout directory ("libs/") and
|
||||
@ -293,7 +293,7 @@ describe('React Applications', () => {
|
||||
xit('should support styled-jsx', async () => {
|
||||
const appName = uniq('app');
|
||||
runCLI(
|
||||
`generate @nx/react:app ${appName} --style=styled-jsx --bundler=vite --no-interactive --skipFormat`
|
||||
`generate @nx/react:app ${appName} --style=styled-jsx --bundler=vite --no-interactive --skipFormat --linter=eslint --unitTestRunner=vitest`
|
||||
);
|
||||
|
||||
// update app to use styled-jsx
|
||||
@ -342,7 +342,7 @@ describe('React Applications', () => {
|
||||
it('should support tailwind', async () => {
|
||||
const appName = uniq('app');
|
||||
runCLI(
|
||||
`generate @nx/react:app apps/${appName} --style=tailwind --bundler=vite --no-interactive --skipFormat`
|
||||
`generate @nx/react:app apps/${appName} --style=tailwind --bundler=vite --no-interactive --skipFormat --linter=eslint --unitTestRunner=vitest`
|
||||
);
|
||||
|
||||
// update app to use styled-jsx
|
||||
@ -386,7 +386,7 @@ describe('React Applications', () => {
|
||||
it('should be formatted on freshly created apps', async () => {
|
||||
const appName = uniq('app');
|
||||
runCLI(
|
||||
`generate @nx/react:app ${appName} --bundler=webpack --no-interactive`
|
||||
`generate @nx/react:app ${appName} --bundler=webpack --no-interactive --linter=eslint --unitTestRunner=jest`
|
||||
);
|
||||
|
||||
const stdout = runCLI(`format:check --projects=${appName}`, {
|
||||
@ -416,15 +416,15 @@ describe('React Applications', () => {
|
||||
const plainJsLib = uniq('jslib');
|
||||
|
||||
runCLI(
|
||||
`generate @nx/react:app apps/${appName} --bundler=webpack --unit-test-runner=jest --no-interactive --js --skipFormat`
|
||||
`generate @nx/react:app apps/${appName} --bundler=webpack --unit-test-runner=jest --no-interactive --js --skipFormat --linter=eslint`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/react:lib libs/${libName} --no-interactive --js --unit-test-runner=none --skipFormat`
|
||||
`generate @nx/react:lib libs/${libName} --no-interactive --js --unit-test-runner=none --skipFormat --linter=eslint`
|
||||
);
|
||||
// Make sure plain JS libs can be imported as well.
|
||||
// There was an issue previously: https://github.com/nrwl/nx/issues/10990
|
||||
runCLI(
|
||||
`generate @nx/js:lib libs/${plainJsLib} --js --unit-test-runner=none --bundler=none --compiler=tsc --no-interactive --skipFormat`
|
||||
`generate @nx/js:lib libs/${plainJsLib} --js --unit-test-runner=none --bundler=none --compiler=tsc --no-interactive --skipFormat --linter=eslint`
|
||||
);
|
||||
|
||||
const mainPath = `apps/${appName}/src/main.js`;
|
||||
@ -450,7 +450,7 @@ describe('React Applications', () => {
|
||||
`('should support global and css modules', async ({ style }) => {
|
||||
const appName = uniq('app');
|
||||
runCLI(
|
||||
`generate @nx/react:app apps/${appName} --style=${style} --bundler=webpack --no-interactive --skipFormat`
|
||||
`generate @nx/react:app apps/${appName} --style=${style} --bundler=webpack --no-interactive --skipFormat --linter=eslint --unitTestRunner=jest`
|
||||
);
|
||||
|
||||
// make sure stylePreprocessorOptions works
|
||||
|
||||
@ -26,7 +26,9 @@ describe('Remix E2E Tests', () => {
|
||||
|
||||
it('should not cause peer dependency conflicts', async () => {
|
||||
const plugin = uniq('remix');
|
||||
runCLI(`generate @nx/remix:app ${plugin}`);
|
||||
runCLI(
|
||||
`generate @nx/remix:app ${plugin} --linter=eslint --unitTestRunner=vitest`
|
||||
);
|
||||
|
||||
await runCommandAsync('npm install');
|
||||
}, 120000);
|
||||
@ -43,7 +45,9 @@ describe('Remix E2E Tests', () => {
|
||||
|
||||
it('should create app', async () => {
|
||||
const plugin = uniq('remix');
|
||||
runCLI(`generate @nx/remix:app ${plugin}`);
|
||||
runCLI(
|
||||
`generate @nx/remix:app ${plugin} --linter=eslint --unitTestRunner=vitest`
|
||||
);
|
||||
|
||||
const buildResult = runCLI(`build ${plugin}`);
|
||||
expect(buildResult).toContain('Successfully ran target build');
|
||||
@ -56,7 +60,7 @@ describe('Remix E2E Tests', () => {
|
||||
it('should create src in the specified directory', async () => {
|
||||
const plugin = uniq('remix');
|
||||
runCLI(
|
||||
`generate @nx/remix:app --name=${plugin} --directory=subdir --rootProject=false --no-interactive`
|
||||
`generate @nx/remix:app --name=${plugin} --directory=subdir --rootProject=false --no-interactive --linter=eslint --unitTestRunner=vitest`
|
||||
);
|
||||
|
||||
const result = runCLI(`build ${plugin}`);
|
||||
@ -69,7 +73,7 @@ describe('Remix E2E Tests', () => {
|
||||
it('should add tags to the project', async () => {
|
||||
const plugin = uniq('remix');
|
||||
runCLI(
|
||||
`generate @nx/remix:app apps/${plugin} --tags e2etag,e2ePackage`
|
||||
`generate @nx/remix:app apps/${plugin} --tags e2etag,e2ePackage --linter=eslint --unitTestRunner=vitest`
|
||||
);
|
||||
const project = readJson(`apps/${plugin}/project.json`);
|
||||
expect(project.tags).toEqual(['e2etag', 'e2ePackage']);
|
||||
@ -79,7 +83,9 @@ describe('Remix E2E Tests', () => {
|
||||
describe('--js', () => {
|
||||
it('should create js app and build correctly', async () => {
|
||||
const plugin = uniq('remix');
|
||||
runCLI(`generate @nx/remix:app ${plugin} --js=true`);
|
||||
runCLI(
|
||||
`generate @nx/remix:app ${plugin} --js=true --linter=eslint --unitTestRunner=vitest`
|
||||
);
|
||||
|
||||
const result = runCLI(`build ${plugin}`);
|
||||
expect(result).toContain('Successfully ran target build');
|
||||
@ -89,7 +95,9 @@ describe('Remix E2E Tests', () => {
|
||||
describe('--unitTestRunner', () => {
|
||||
it('should generate a library with vitest and test correctly', async () => {
|
||||
const plugin = uniq('remix');
|
||||
runCLI(`generate @nx/remix:library ${plugin} --unitTestRunner=vitest`);
|
||||
runCLI(
|
||||
`generate @nx/remix:library ${plugin} --unitTestRunner=vitest --linter=eslint`
|
||||
);
|
||||
|
||||
const result = runCLI(`test ${plugin}`);
|
||||
expect(result).toContain(`Successfully ran target test`);
|
||||
@ -98,11 +106,11 @@ describe('Remix E2E Tests', () => {
|
||||
it('should generate a library with jest and test correctly', async () => {
|
||||
const reactapp = uniq('react');
|
||||
runCLI(
|
||||
`generate @nx/react:application ${reactapp} --unitTestRunner=jest`
|
||||
`generate @nx/react:application ${reactapp} --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
const plugin = uniq('remix');
|
||||
runCLI(
|
||||
`generate @nx/remix:application ${plugin} --unitTestRunner=jest`
|
||||
`generate @nx/remix:application ${plugin} --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
|
||||
const result = runCLI(`test ${plugin}`);
|
||||
@ -118,7 +126,7 @@ describe('Remix E2E Tests', () => {
|
||||
|
||||
beforeAll(async () => {
|
||||
runCLI(
|
||||
`generate @nx/remix:app apps/${plugin} --tags e2etag,e2ePackage`
|
||||
`generate @nx/remix:app apps/${plugin} --tags e2etag,e2ePackage --linter=eslint --unitTestRunner=vitest`
|
||||
);
|
||||
}, 120000);
|
||||
|
||||
|
||||
@ -67,9 +67,9 @@ describe('rspack e2e', () => {
|
||||
// Make sure expected files are present.
|
||||
/**
|
||||
* The files that are generated are:
|
||||
* ["3rdpartylicenses.txt", "assets", "favicon.ico", "index.html", "main.bf7851e6.js", "runtime.e4294127.js"]
|
||||
* ["assets", "favicon.ico", "index.html", "main.bf7851e6.js", "runtime.e4294127.js"]
|
||||
*/
|
||||
expect(listFiles(`dist/${project}`)).toHaveLength(6);
|
||||
expect(listFiles(`dist/${project}`)).toHaveLength(5);
|
||||
|
||||
result = runCLI(`test ${project}`);
|
||||
expect(result).toContain('Successfully ran target test');
|
||||
@ -87,7 +87,7 @@ describe('rspack e2e', () => {
|
||||
env: { NODE_ENV: 'production' },
|
||||
});
|
||||
expect(result).toContain('Successfully ran target build');
|
||||
expect(listFiles(`dist/${project}`)).toHaveLength(6); // same length as before
|
||||
expect(listFiles(`dist/${project}`)).toHaveLength(5); // same length as before
|
||||
|
||||
// Generate a new app and check that the files are correct
|
||||
const app2 = uniq('app2');
|
||||
@ -120,7 +120,7 @@ describe('rspack e2e', () => {
|
||||
});
|
||||
expect(result).toContain('Successfully ran target build');
|
||||
// Make sure expected files are present.
|
||||
expect(listFiles(`dist/${app2}`)).toHaveLength(6);
|
||||
expect(listFiles(`dist/${app2}`)).toHaveLength(5);
|
||||
|
||||
result = runCLI(`test ${app2}`);
|
||||
expect(result).toContain('Successfully ran target test');
|
||||
@ -139,11 +139,11 @@ describe('rspack e2e', () => {
|
||||
result = runCLI(`build ${app3}`);
|
||||
expect(result).toContain('Successfully ran target build');
|
||||
// Make sure expected files are present.
|
||||
expect(listFiles(`dist/${app3}`)).toHaveLength(3);
|
||||
expect(listFiles(`dist/${app3}`)).toHaveLength(2);
|
||||
|
||||
result = runCLI(`build ${app3} --generatePackageJson=true`);
|
||||
expect(result).toContain('Successfully ran target build');
|
||||
// Make sure expected files are present.
|
||||
expect(listFiles(`dist/${app3}`)).toHaveLength(5);
|
||||
expect(listFiles(`dist/${app3}`)).toHaveLength(4);
|
||||
}, 200_000);
|
||||
});
|
||||
|
||||
@ -222,6 +222,8 @@ export function runCreateWorkspace(
|
||||
docker,
|
||||
nextAppDir,
|
||||
nextSrcDir,
|
||||
linter = 'eslint',
|
||||
formatter = 'prettier',
|
||||
e2eTestRunner,
|
||||
ssr,
|
||||
framework,
|
||||
@ -241,7 +243,9 @@ export function runCreateWorkspace(
|
||||
docker?: boolean;
|
||||
nextAppDir?: boolean;
|
||||
nextSrcDir?: boolean;
|
||||
linter?: 'none' | 'eslint';
|
||||
e2eTestRunner?: 'cypress' | 'playwright' | 'jest' | 'detox' | 'none';
|
||||
formatter?: 'prettier' | 'none';
|
||||
ssr?: boolean;
|
||||
framework?: string;
|
||||
prefix?: string;
|
||||
@ -291,6 +295,14 @@ export function runCreateWorkspace(
|
||||
command += ` --package-manager=${packageManager}`;
|
||||
}
|
||||
|
||||
if (linter) {
|
||||
command += ` --linter=${linter}`;
|
||||
}
|
||||
|
||||
if (formatter) {
|
||||
command += ` --formatter=${formatter}`;
|
||||
}
|
||||
|
||||
if (e2eTestRunner) {
|
||||
command += ` --e2eTestRunner=${e2eTestRunner}`;
|
||||
}
|
||||
|
||||
@ -48,7 +48,9 @@ describe('Vite Plugin', () => {
|
||||
|
||||
beforeAll(() => {
|
||||
myApp = uniq('my-app');
|
||||
runCLI(`generate @nx/react:app ${myApp} --bundler=vite`);
|
||||
runCLI(
|
||||
`generate @nx/react:app ${myApp} --bundler=vite --unitTestRunner=vitest`
|
||||
);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@ -95,7 +97,7 @@ describe('Vite Plugin', () => {
|
||||
beforeEach(() => {
|
||||
myApp = uniq('my-app');
|
||||
runCLI(
|
||||
`generate @nx/web:app ${myApp} --bundler=vite --directory=${myApp}`
|
||||
`generate @nx/web:app ${myApp} --bundler=vite --unitTestRunner=vitest --directory=${myApp}`
|
||||
);
|
||||
});
|
||||
it('should build application', async () => {
|
||||
@ -187,7 +189,7 @@ describe('Vite Plugin', () => {
|
||||
packages: ['@nx/react'],
|
||||
});
|
||||
runCLI(
|
||||
`generate @nx/react:app ${app} --bundler=vite --no-interactive --directory=${app}`
|
||||
`generate @nx/react:app ${app} --bundler=vite --unitTestRunner=vitest --no-interactive --directory=${app}`
|
||||
);
|
||||
|
||||
// only this project will be directly used from dist
|
||||
|
||||
@ -24,10 +24,10 @@ describe('Webpack Plugin (legacy)', () => {
|
||||
packages: ['@nx/react'],
|
||||
});
|
||||
runCLI(
|
||||
`generate @nx/react:app ${appName} --bundler webpack --e2eTestRunner=cypress --rootProject --no-interactive`
|
||||
`generate @nx/react:app ${appName} --bundler webpack --e2eTestRunner=cypress --rootProject --no-interactive --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
runCLI(
|
||||
`generate @nx/react:lib ${libName} --unitTestRunner jest --no-interactive`
|
||||
`generate @nx/react:lib ${libName} --unitTestRunner jest --no-interactive --linter=eslint`
|
||||
);
|
||||
});
|
||||
|
||||
@ -72,7 +72,9 @@ describe('Webpack Plugin (legacy)', () => {
|
||||
// Issue: https://github.com/nrwl/nx/issues/20179
|
||||
it('should allow main/styles entries to be spread within composePlugins() function (#20179)', () => {
|
||||
const appName = uniq('app');
|
||||
runCLI(`generate @nx/web:app ${appName} --bundler webpack`);
|
||||
runCLI(
|
||||
`generate @nx/web:app ${appName} --bundler webpack --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
|
||||
checkFilesExist(`${appName}/src/main.ts`);
|
||||
updateFile(`${appName}/src/main.ts`, `console.log('Hello');\n`);
|
||||
@ -109,7 +111,7 @@ describe('Webpack Plugin (legacy)', () => {
|
||||
it('should support standard webpack config with executors', () => {
|
||||
const appName = uniq('app');
|
||||
runCLI(
|
||||
`generate @nx/web:app ${appName} --bundler webpack --e2eTestRunner=playwright`
|
||||
`generate @nx/web:app ${appName} --bundler webpack --e2eTestRunner=playwright --unitTestRunner=jest --linter=eslint`
|
||||
);
|
||||
updateFile(
|
||||
`${appName}/src/main.ts`,
|
||||
@ -153,7 +155,7 @@ describe('Webpack Plugin (legacy)', () => {
|
||||
it('should convert withNx webpack config to a standard config using NxWebpackPlugin', () => {
|
||||
const appName = 'app3224373'; // Needs to be reserved so that the snapshot projectName matches
|
||||
runCLI(
|
||||
`generate @nx/web:app ${appName} --bundler webpack --e2eTestRunner=playwright`
|
||||
`generate @nx/web:app ${appName} --bundler webpack --e2eTestRunner=playwright --unitTestRunner=vitest --linter=eslint`
|
||||
);
|
||||
updateFile(
|
||||
`${appName}/src/main.ts`,
|
||||
|
||||
@ -52,6 +52,9 @@ interface ReactArguments extends BaseArguments {
|
||||
nextAppDir: boolean;
|
||||
nextSrcDir: boolean;
|
||||
e2eTestRunner: 'none' | 'cypress' | 'playwright';
|
||||
linter?: 'none' | 'eslint';
|
||||
formatter?: 'none' | 'prettier';
|
||||
workspaces?: boolean;
|
||||
}
|
||||
|
||||
interface AngularArguments extends BaseArguments {
|
||||
@ -157,6 +160,15 @@ export const commandsObject: yargs.Argv<Arguments> = yargs
|
||||
describe: chalk.dim`Bundler to be used to build the app.`,
|
||||
type: 'string',
|
||||
})
|
||||
.option('workspaces', {
|
||||
describe: chalk.dim`Use package manager workspaces.`,
|
||||
type: 'boolean',
|
||||
default: false,
|
||||
})
|
||||
.option('formatter', {
|
||||
describe: chalk.dim`Code formatter to use.`,
|
||||
type: 'string',
|
||||
})
|
||||
.option('framework', {
|
||||
describe: chalk.dim`Framework option to be used with certain stacks.`,
|
||||
type: 'string',
|
||||
@ -440,14 +452,11 @@ async function determinePresetOptions(
|
||||
}
|
||||
}
|
||||
|
||||
async function determineNoneOptions(
|
||||
parsedArgs: yargs.Arguments<NoneArguments>
|
||||
): Promise<Partial<NoneArguments>> {
|
||||
if (
|
||||
(!parsedArgs.preset || parsedArgs.preset === Preset.TS) &&
|
||||
process.env.NX_ADD_PLUGINS !== 'false' &&
|
||||
process.env.NX_ADD_TS_PLUGIN !== 'false'
|
||||
) {
|
||||
async function determineFormatterOptions(args: {
|
||||
formatter?: 'none' | 'prettier';
|
||||
interactive?: boolean;
|
||||
}) {
|
||||
if (args.formatter) return args.formatter;
|
||||
const reply = await enquirer.prompt<{ prettier: 'Yes' | 'No' }>([
|
||||
{
|
||||
name: 'prettier',
|
||||
@ -462,12 +471,44 @@ async function determineNoneOptions(
|
||||
},
|
||||
],
|
||||
initial: 1,
|
||||
skip: !parsedArgs.interactive || isCI(),
|
||||
skip: !args.interactive || isCI(),
|
||||
},
|
||||
]);
|
||||
return reply.prettier === 'Yes' ? 'prettier' : 'none';
|
||||
}
|
||||
|
||||
async function determineLinterOptions(args: { interactive?: boolean }) {
|
||||
const reply = await enquirer.prompt<{ eslint: 'Yes' | 'No' }>([
|
||||
{
|
||||
name: 'eslint',
|
||||
message: `Would you like to use ESLint?`,
|
||||
type: 'autocomplete',
|
||||
choices: [
|
||||
{
|
||||
name: 'Yes',
|
||||
},
|
||||
{
|
||||
name: 'No',
|
||||
},
|
||||
],
|
||||
initial: 1,
|
||||
skip: !args.interactive || isCI(),
|
||||
},
|
||||
]);
|
||||
return reply.eslint === 'Yes' ? 'eslint' : 'none';
|
||||
}
|
||||
|
||||
async function determineNoneOptions(
|
||||
parsedArgs: yargs.Arguments<NoneArguments>
|
||||
): Promise<Partial<NoneArguments>> {
|
||||
if (
|
||||
(!parsedArgs.preset || parsedArgs.preset === Preset.TS) &&
|
||||
process.env.NX_ADD_PLUGINS !== 'false' &&
|
||||
process.env.NX_ADD_TS_PLUGIN !== 'false'
|
||||
) {
|
||||
return {
|
||||
preset: Preset.TS,
|
||||
formatter: reply.prettier === 'Yes' ? 'prettier' : 'none',
|
||||
formatter: await determineFormatterOptions(parsedArgs),
|
||||
};
|
||||
} else {
|
||||
let preset: Preset;
|
||||
@ -535,6 +576,10 @@ async function determineReactOptions(
|
||||
let e2eTestRunner: undefined | 'none' | 'cypress' | 'playwright' = undefined;
|
||||
let nextAppDir = false;
|
||||
let nextSrcDir = false;
|
||||
let linter: undefined | 'none' | 'eslint';
|
||||
let formatter: undefined | 'none' | 'prettier';
|
||||
|
||||
const workspaces = parsedArgs.workspaces ?? false;
|
||||
|
||||
if (parsedArgs.preset && parsedArgs.preset !== Preset.React) {
|
||||
preset = parsedArgs.preset;
|
||||
@ -550,27 +595,25 @@ async function determineReactOptions(
|
||||
} else {
|
||||
const framework = await determineReactFramework(parsedArgs);
|
||||
|
||||
// React Native and Expo only support integrated monorepos for now.
|
||||
// TODO(jack): Add standalone support for React Native and Expo.
|
||||
const workspaceType =
|
||||
framework === 'react-native' || framework === 'expo'
|
||||
? 'integrated'
|
||||
: await determineStandaloneOrMonorepo();
|
||||
const isStandalone =
|
||||
workspaces || framework === 'react-native' || framework === 'expo'
|
||||
? false
|
||||
: (await determineStandaloneOrMonorepo()) === 'standalone';
|
||||
|
||||
if (workspaceType === 'standalone') {
|
||||
if (isStandalone) {
|
||||
appName = parsedArgs.name;
|
||||
} else {
|
||||
appName = await determineAppName(parsedArgs);
|
||||
}
|
||||
|
||||
if (framework === 'nextjs') {
|
||||
if (workspaceType === 'standalone') {
|
||||
if (isStandalone) {
|
||||
preset = Preset.NextJsStandalone;
|
||||
} else {
|
||||
preset = Preset.NextJs;
|
||||
}
|
||||
} else if (framework === 'remix') {
|
||||
if (workspaceType === 'standalone') {
|
||||
if (isStandalone) {
|
||||
preset = Preset.RemixStandalone;
|
||||
} else {
|
||||
preset = Preset.RemixMonorepo;
|
||||
@ -580,7 +623,7 @@ async function determineReactOptions(
|
||||
} else if (framework === 'expo') {
|
||||
preset = Preset.Expo;
|
||||
} else {
|
||||
if (workspaceType === 'standalone') {
|
||||
if (isStandalone) {
|
||||
preset = Preset.ReactStandalone;
|
||||
} else {
|
||||
preset = Preset.ReactMonorepo;
|
||||
@ -657,6 +700,14 @@ async function determineReactOptions(
|
||||
style = reply.style;
|
||||
}
|
||||
|
||||
if (workspaces) {
|
||||
linter = await determineLinterOptions(parsedArgs);
|
||||
formatter = await determineFormatterOptions(parsedArgs);
|
||||
} else {
|
||||
linter = 'eslint';
|
||||
formatter = 'prettier';
|
||||
}
|
||||
|
||||
return {
|
||||
preset,
|
||||
style,
|
||||
@ -665,6 +716,9 @@ async function determineReactOptions(
|
||||
nextAppDir,
|
||||
nextSrcDir,
|
||||
e2eTestRunner,
|
||||
linter,
|
||||
formatter,
|
||||
workspaces,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ import { setupCI } from './utils/ci/setup-ci';
|
||||
import { initializeGitRepo } from './utils/git/git';
|
||||
import { getPackageNameFromThirdPartyPreset } from './utils/preset/get-third-party-preset';
|
||||
import { mapErrorToBodyLines } from './utils/error-utils';
|
||||
import { Preset } from './utils/preset/preset';
|
||||
|
||||
export async function createWorkspace<T extends CreateWorkspaceOptions>(
|
||||
preset: string,
|
||||
@ -30,12 +31,14 @@ export async function createWorkspace<T extends CreateWorkspaceOptions>(
|
||||
|
||||
const tmpDir = await createSandbox(packageManager);
|
||||
|
||||
const workspaceGlobs = getWorkspaceGlobsFromPreset(preset);
|
||||
|
||||
// nx new requires a preset currently. We should probably make it optional.
|
||||
const directory = await createEmptyWorkspace<T>(
|
||||
tmpDir,
|
||||
name,
|
||||
packageManager,
|
||||
{ ...options, preset }
|
||||
{ ...options, preset, workspaceGlobs }
|
||||
);
|
||||
|
||||
// If the preset is a third-party preset, we need to call createPreset to install it
|
||||
@ -96,3 +99,24 @@ export function extractConnectUrl(text: string): string | null {
|
||||
const match = text.match(urlPattern);
|
||||
return match ? match[0] : null;
|
||||
}
|
||||
|
||||
function getWorkspaceGlobsFromPreset(preset: string): string[] {
|
||||
// Should match how apps are created in `packages/workspace/src/generators/preset/preset.ts`.
|
||||
switch (preset) {
|
||||
case Preset.AngularMonorepo:
|
||||
case Preset.Expo:
|
||||
case Preset.Express:
|
||||
case Preset.Nest:
|
||||
case Preset.NextJs:
|
||||
case Preset.NodeMonorepo:
|
||||
case Preset.Nuxt:
|
||||
case Preset.ReactNative:
|
||||
case Preset.ReactMonorepo:
|
||||
case Preset.RemixMonorepo:
|
||||
case Preset.VueMonorepo:
|
||||
case Preset.WebComponents:
|
||||
return ['apps/**', 'packages/**'];
|
||||
default:
|
||||
return ['packages/**'];
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,5 +16,6 @@
|
||||
"<%= offsetFromProjectRoot %>**/*.cy.js",
|
||||
<%_ if (jsx) { _%>"<%= offsetFromProjectRoot %>**/*.cy.jsx",<%_ } _%>
|
||||
"<%= offsetFromProjectRoot %>**/*.d.ts"
|
||||
]
|
||||
],
|
||||
"exclude": ["dist"<% if (linter === 'eslint') { %>, "eslint.config.js"<% } %>]
|
||||
}
|
||||
|
||||
@ -105,6 +105,26 @@ export async function configurationGeneratorInternal(
|
||||
addTarget(tree, opts, projectGraph);
|
||||
}
|
||||
|
||||
const projectTsConfigPath = joinPathFragments(
|
||||
opts.projectRoot,
|
||||
'tsconfig.json'
|
||||
);
|
||||
if (tree.exists(projectTsConfigPath)) {
|
||||
updateJson(tree, projectTsConfigPath, (json) => {
|
||||
// Cypress uses commonjs, unless the project is also using commonjs (or does not set "module" i.e. uses default of commonjs),
|
||||
// then we need to set the moduleResolution to node10 or else Cypress will fail with TS5095 error.
|
||||
// See: https://github.com/cypress-io/cypress/issues/27731
|
||||
if (
|
||||
(json.compilerOptions?.module ||
|
||||
json.compilerOptions?.module !== 'commonjs') &&
|
||||
json.compilerOptions?.moduleResolution
|
||||
) {
|
||||
json.compilerOptions.moduleResolution = 'node10';
|
||||
}
|
||||
return json;
|
||||
});
|
||||
}
|
||||
|
||||
const { root: projectRoot } = readProjectConfiguration(tree, options.project);
|
||||
const isTsSolutionSetup = isUsingTsSolutionSetup(tree);
|
||||
if (isTsSolutionSetup) {
|
||||
@ -201,6 +221,7 @@ In this case you need to provide a devServerTarget,'<projectName>:<targetName>[:
|
||||
return {
|
||||
...options,
|
||||
bundler: options.bundler ?? 'webpack',
|
||||
projectRoot: projectConfig.root,
|
||||
rootProject: options.rootProject ?? projectConfig.root === '.',
|
||||
linter,
|
||||
devServerTarget,
|
||||
@ -408,6 +429,9 @@ function createPackageJson(tree: Tree, options: NormalizedSchema) {
|
||||
name: importPath,
|
||||
version: '0.0.1',
|
||||
private: true,
|
||||
nx: {
|
||||
name: options.project,
|
||||
},
|
||||
};
|
||||
writeJson(tree, packageJsonPath, packageJson);
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { formatFiles, runTasksInSerial, Tree } from '@nx/devkit';
|
||||
import { assertNotUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
import { initGenerator as jsInitGenerator } from '@nx/js';
|
||||
|
||||
import detoxInitGenerator from '../init/init';
|
||||
import { addGitIgnoreEntry } from './lib/add-git-ignore-entry';
|
||||
@ -21,7 +21,9 @@ export async function detoxApplicationGeneratorInternal(
|
||||
host: Tree,
|
||||
schema: Schema
|
||||
) {
|
||||
assertNotUsingTsSolutionSetup(host, 'detox', 'application');
|
||||
const jsInitTask = await jsInitGenerator(host, {
|
||||
skipFormat: true,
|
||||
});
|
||||
|
||||
const options = await normalizeOptions(host, schema);
|
||||
|
||||
@ -40,7 +42,7 @@ export async function detoxApplicationGeneratorInternal(
|
||||
await formatFiles(host);
|
||||
}
|
||||
|
||||
return runTasksInSerial(initTask, lintingTask, depsTask);
|
||||
return runTasksInSerial(jsInitTask, initTask, lintingTask, depsTask);
|
||||
}
|
||||
|
||||
export default detoxApplicationGenerator;
|
||||
|
||||
@ -1,8 +1,10 @@
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
joinPathFragments,
|
||||
readNxJson,
|
||||
TargetConfiguration,
|
||||
Tree,
|
||||
writeJson,
|
||||
} from '@nx/devkit';
|
||||
import {
|
||||
expoBuildTarget,
|
||||
@ -11,6 +13,7 @@ import {
|
||||
reactNativeTestTarget,
|
||||
} from './get-targets';
|
||||
import { NormalizedSchema } from './normalize-options';
|
||||
import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
|
||||
export function addProject(host: Tree, options: NormalizedSchema) {
|
||||
const nxJson = readNxJson(host);
|
||||
@ -20,6 +23,19 @@ export function addProject(host: Tree, options: NormalizedSchema) {
|
||||
: p.plugin === '@nx/detox/plugin'
|
||||
);
|
||||
|
||||
if (isUsingTsSolutionSetup(host)) {
|
||||
writeJson(host, joinPathFragments(options.e2eProjectRoot, 'package.json'), {
|
||||
name: options.e2eProjectName,
|
||||
version: '0.0.1',
|
||||
private: true,
|
||||
nx: {
|
||||
sourceRoot: `${options.e2eProjectRoot}/src`,
|
||||
projectType: 'application',
|
||||
targets: hasPlugin ? undefined : getTargets(options),
|
||||
implicitDependencies: [options.appProject],
|
||||
},
|
||||
});
|
||||
} else {
|
||||
addProjectConfiguration(host, options.e2eProjectName, {
|
||||
root: options.e2eProjectRoot,
|
||||
sourceRoot: `${options.e2eProjectRoot}/src`,
|
||||
@ -29,6 +45,7 @@ export function addProject(host: Tree, options: NormalizedSchema) {
|
||||
implicitDependencies: [options.appProject],
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getTargets(options: NormalizedSchema) {
|
||||
const targets: { [key: string]: TargetConfiguration } = {};
|
||||
|
||||
@ -9,7 +9,6 @@ import {
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
import { addPluginV1 } from '@nx/devkit/src/utils/add-plugin';
|
||||
import { assertNotUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
import { createNodes } from '../../plugins/plugin';
|
||||
import { detoxVersion, nxVersion } from '../../utils/versions';
|
||||
import { Schema } from './schema';
|
||||
@ -19,8 +18,6 @@ export function detoxInitGenerator(host: Tree, schema: Schema) {
|
||||
}
|
||||
|
||||
export async function detoxInitGeneratorInternal(host: Tree, schema: Schema) {
|
||||
assertNotUsingTsSolutionSetup(host, 'detox', 'init');
|
||||
|
||||
const tasks: GeneratorCallback[] = [];
|
||||
|
||||
const nxJson = readNxJson(host);
|
||||
|
||||
@ -90,6 +90,7 @@ export async function lintWorkspaceRulesProjectGenerator(
|
||||
join(workspaceLintPluginDir, 'tsconfig.spec.json'),
|
||||
(json) => {
|
||||
delete json.compilerOptions?.module;
|
||||
delete json.compilerOptions?.moduleResolution;
|
||||
|
||||
if (json.include) {
|
||||
json.include = json.include.map((v) => {
|
||||
|
||||
@ -6,6 +6,8 @@ import {
|
||||
readNxJson,
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
updateJson,
|
||||
writeJson,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { Linter } from '@nx/eslint';
|
||||
@ -327,4 +329,147 @@ describe('app', () => {
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TS solution setup', () => {
|
||||
it('should add project references when using TS solution', async () => {
|
||||
const tree = createTreeWithEmptyWorkspace();
|
||||
tree.write('.gitignore', '');
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
json.workspaces = ['packages/*', 'apps/*'];
|
||||
return json;
|
||||
});
|
||||
writeJson(tree, 'tsconfig.base.json', {
|
||||
compilerOptions: {
|
||||
composite: true,
|
||||
declaration: true,
|
||||
},
|
||||
});
|
||||
writeJson(tree, 'tsconfig.json', {
|
||||
extends: './tsconfig.base.json',
|
||||
files: [],
|
||||
references: [],
|
||||
});
|
||||
|
||||
await expoApplicationGenerator(tree, {
|
||||
directory: 'my-app',
|
||||
displayName: 'myApp',
|
||||
linter: Linter.EsLint,
|
||||
e2eTestRunner: 'none',
|
||||
skipFormat: false,
|
||||
js: false,
|
||||
unitTestRunner: 'jest',
|
||||
addPlugin: true,
|
||||
});
|
||||
|
||||
expect(readJson(tree, 'tsconfig.json').references).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"path": "./my-app",
|
||||
},
|
||||
]
|
||||
`);
|
||||
expect(readJson(tree, 'my-app/tsconfig.json')).toMatchInlineSnapshot(`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"declaration": true,
|
||||
"jsx": "react-native",
|
||||
"lib": [
|
||||
"dom",
|
||||
"esnext",
|
||||
],
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
},
|
||||
"extends": "../tsconfig.base.json",
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json",
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
expect(readJson(tree, 'my-app/tsconfig.app.json')).toMatchInlineSnapshot(`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"noUnusedLocals": false,
|
||||
"outDir": "out-tsc/my-app",
|
||||
"rootDir": "src",
|
||||
"types": [
|
||||
"node",
|
||||
],
|
||||
},
|
||||
"exclude": [
|
||||
"dist",
|
||||
"jest.config.ts",
|
||||
"**/*.spec.ts",
|
||||
"**/*.spec.tsx",
|
||||
"src/test-setup.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.ts",
|
||||
"eslint.config.js",
|
||||
"eslint.config.cjs",
|
||||
"eslint.config.mjs",
|
||||
],
|
||||
"extends": "../tsconfig.base.json",
|
||||
"files": [
|
||||
"../node_modules/@nx/expo/typings/svg.d.ts",
|
||||
],
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.tsx",
|
||||
"**/*.js",
|
||||
"**/*.jsx",
|
||||
],
|
||||
}
|
||||
`);
|
||||
expect(readJson(tree, 'my-app/tsconfig.spec.json'))
|
||||
.toMatchInlineSnapshot(`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"noUnusedLocals": false,
|
||||
"outDir": "./out-tsc/jest",
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
],
|
||||
},
|
||||
"extends": "../tsconfig.base.json",
|
||||
"files": [
|
||||
"src/test-setup.ts",
|
||||
],
|
||||
"include": [
|
||||
"jest.config.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.d.ts",
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -6,7 +6,7 @@ import {
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
import { initGenerator as jsInitGenerator } from '@nx/js';
|
||||
import { assertNotUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
import { updateTsconfigFiles } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
|
||||
import { addLinting } from '../../utils/add-linting';
|
||||
import { addJest } from '../../utils/add-jest';
|
||||
@ -36,16 +36,16 @@ export async function expoApplicationGeneratorInternal(
|
||||
host: Tree,
|
||||
schema: Schema
|
||||
): Promise<GeneratorCallback> {
|
||||
assertNotUsingTsSolutionSetup(host, 'expo', 'application');
|
||||
|
||||
const options = await normalizeOptions(host, schema);
|
||||
|
||||
const tasks: GeneratorCallback[] = [];
|
||||
const jsInitTask = await jsInitGenerator(host, {
|
||||
...schema,
|
||||
skipFormat: true,
|
||||
addTsPlugin: schema.useTsSolution,
|
||||
formatter: schema.formatter,
|
||||
});
|
||||
|
||||
const options = await normalizeOptions(host, schema);
|
||||
|
||||
tasks.push(jsInitTask);
|
||||
const initTask = await initGenerator(host, { ...options, skipFormat: true });
|
||||
tasks.push(initTask);
|
||||
@ -80,6 +80,21 @@ export async function expoApplicationGeneratorInternal(
|
||||
tasks.push(e2eTask);
|
||||
addEasScripts(host);
|
||||
|
||||
updateTsconfigFiles(
|
||||
host,
|
||||
options.appProjectRoot,
|
||||
'tsconfig.app.json',
|
||||
{
|
||||
jsx: 'react-jsx',
|
||||
module: 'esnext',
|
||||
moduleResolution: 'bundler',
|
||||
noUnusedLocals: false,
|
||||
},
|
||||
options.linter === 'eslint'
|
||||
? ['eslint.config.js', 'eslint.config.cjs', 'eslint.config.mjs']
|
||||
: undefined
|
||||
);
|
||||
|
||||
if (!options.skipFormat) {
|
||||
await formatFiles(host);
|
||||
}
|
||||
|
||||
@ -1,10 +1,11 @@
|
||||
import { GeneratorCallback, Tree } from '@nx/devkit';
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
ensurePackage,
|
||||
getPackageManagerCommand,
|
||||
GeneratorCallback,
|
||||
joinPathFragments,
|
||||
readNxJson,
|
||||
Tree,
|
||||
writeJson,
|
||||
} from '@nx/devkit';
|
||||
import { webStaticServeGenerator } from '@nx/web';
|
||||
|
||||
@ -14,6 +15,7 @@ import { NormalizedSchema } from './normalize-options';
|
||||
import { addE2eCiTargetDefaults } from '@nx/devkit/src/generators/target-defaults-utils';
|
||||
import { findPluginForConfigFile } from '@nx/devkit/src/utils/find-plugin-for-config-file';
|
||||
import { getE2EWebServerInfo } from '@nx/devkit/src/generators/e2e-web-server-info-utils';
|
||||
import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
|
||||
export async function addE2e(
|
||||
tree: Tree,
|
||||
@ -40,6 +42,22 @@ export async function addE2e(
|
||||
typeof import('@nx/cypress')
|
||||
>('@nx/cypress', nxVersion);
|
||||
|
||||
if (isUsingTsSolutionSetup(tree)) {
|
||||
writeJson(
|
||||
tree,
|
||||
joinPathFragments(options.e2eProjectRoot, 'package.json'),
|
||||
{
|
||||
name: options.e2eProjectName,
|
||||
version: '0.0.1',
|
||||
private: true,
|
||||
nx: {
|
||||
projectType: 'application',
|
||||
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),
|
||||
implicitDependencies: [options.projectName],
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
addProjectConfiguration(tree, options.e2eProjectName, {
|
||||
projectType: 'application',
|
||||
root: options.e2eProjectRoot,
|
||||
@ -48,6 +66,7 @@ export async function addE2e(
|
||||
implicitDependencies: [options.projectName],
|
||||
tags: [],
|
||||
});
|
||||
}
|
||||
|
||||
const e2eTask = await configurationGenerator(tree, {
|
||||
...options,
|
||||
@ -106,6 +125,22 @@ export async function addE2e(
|
||||
const { configurationGenerator } = ensurePackage<
|
||||
typeof import('@nx/playwright')
|
||||
>('@nx/playwright', nxVersion);
|
||||
if (isUsingTsSolutionSetup(tree)) {
|
||||
writeJson(
|
||||
tree,
|
||||
joinPathFragments(options.e2eProjectRoot, 'package.json'),
|
||||
{
|
||||
name: options.e2eProjectName,
|
||||
version: '0.0.1',
|
||||
private: true,
|
||||
nx: {
|
||||
projectType: 'application',
|
||||
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),
|
||||
implicitDependencies: [options.projectName],
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
addProjectConfiguration(tree, options.e2eProjectName, {
|
||||
projectType: 'application',
|
||||
root: options.e2eProjectRoot,
|
||||
@ -113,6 +148,7 @@ export async function addE2e(
|
||||
targets: {},
|
||||
implicitDependencies: [options.projectName],
|
||||
});
|
||||
}
|
||||
|
||||
const e2eTask = await configurationGenerator(tree, {
|
||||
project: options.e2eProjectName,
|
||||
|
||||
@ -1,14 +1,18 @@
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
joinPathFragments,
|
||||
ProjectConfiguration,
|
||||
readNxJson,
|
||||
TargetConfiguration,
|
||||
Tree,
|
||||
writeJson,
|
||||
} from '@nx/devkit';
|
||||
|
||||
import { hasExpoPlugin } from '../../../utils/has-expo-plugin';
|
||||
import { NormalizedSchema } from './normalize-options';
|
||||
import { addBuildTargetDefaults } from '@nx/devkit/src/generators/target-defaults-utils';
|
||||
import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
import { getImportPath } from '@nx/js/src/utils/get-import-path';
|
||||
|
||||
export function addProject(host: Tree, options: NormalizedSchema) {
|
||||
const nxJson = readNxJson(host);
|
||||
@ -26,6 +30,21 @@ export function addProject(host: Tree, options: NormalizedSchema) {
|
||||
tags: options.parsedTags,
|
||||
};
|
||||
|
||||
if (isUsingTsSolutionSetup(host)) {
|
||||
const packageName = getImportPath(host, options.name);
|
||||
writeJson(host, joinPathFragments(options.appProjectRoot, 'package.json'), {
|
||||
name: packageName,
|
||||
version: '0.0.1',
|
||||
private: true,
|
||||
nx: {
|
||||
name: packageName === options.name ? undefined : options.name,
|
||||
projectType: 'application',
|
||||
sourceRoot: `${options.appProjectRoot}/src`,
|
||||
targets: hasPlugin ? undefined : getTargets(options),
|
||||
tags: options.parsedTags?.length ? options.parsedTags : undefined,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
addProjectConfiguration(
|
||||
host,
|
||||
options.projectName,
|
||||
@ -33,6 +52,7 @@ export function addProject(host: Tree, options: NormalizedSchema) {
|
||||
options.standaloneConfig
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function getTargets(options: NormalizedSchema) {
|
||||
const architect: { [key: string]: TargetConfiguration } = {};
|
||||
|
||||
@ -15,6 +15,9 @@ export interface Schema {
|
||||
e2eTestRunner: 'cypress' | 'playwright' | 'detox' | 'none'; // default is none
|
||||
standaloneConfig?: boolean;
|
||||
skipPackageJson?: boolean; // default is false
|
||||
// Internal options
|
||||
addPlugin?: boolean;
|
||||
nxCloudToken?: string;
|
||||
useTsSolution?: boolean;
|
||||
formatter?: 'prettier' | 'none';
|
||||
}
|
||||
|
||||
@ -44,13 +44,15 @@
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "eslint"
|
||||
"default": "none",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"description": "Test runner to use for unit tests",
|
||||
"default": "jest"
|
||||
"default": "none",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"tags": {
|
||||
"type": "string",
|
||||
@ -71,7 +73,8 @@
|
||||
"description": "Adds the specified e2e test runner",
|
||||
"type": "string",
|
||||
"enum": ["playwright", "cypress", "detox", "none"],
|
||||
"default": "none"
|
||||
"default": "none",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"standaloneConfig": {
|
||||
"description": "Split the project configuration into `<projectRoot>/project.json` rather than including it inside `workspace.json`.",
|
||||
|
||||
@ -9,7 +9,6 @@ import {
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
import { addPluginV1 } from '@nx/devkit/src/utils/add-plugin';
|
||||
import { assertNotUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
import { createNodes } from '../../../plugins/plugin';
|
||||
import {
|
||||
expoCliVersion,
|
||||
@ -28,8 +27,6 @@ export function expoInitGenerator(tree: Tree, schema: Schema) {
|
||||
}
|
||||
|
||||
export async function expoInitGeneratorInternal(host: Tree, schema: Schema) {
|
||||
assertNotUsingTsSolutionSetup(host, 'expo', 'init');
|
||||
|
||||
const nxJson = readNxJson(host);
|
||||
const addPluginDefault =
|
||||
process.env.NX_ADD_PLUGINS !== 'false' &&
|
||||
|
||||
@ -4,6 +4,7 @@ import {
|
||||
ensureProjectName,
|
||||
} from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||
import { Schema } from '../schema';
|
||||
import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
|
||||
export interface NormalizedSchema extends Schema {
|
||||
name: string;
|
||||
@ -12,6 +13,7 @@ export interface NormalizedSchema extends Schema {
|
||||
routePath: string;
|
||||
parsedTags: string[];
|
||||
appMain: string;
|
||||
isUsingTsSolutionConfig: boolean;
|
||||
}
|
||||
|
||||
export async function normalizeOptions(
|
||||
@ -50,6 +52,7 @@ export async function normalizeOptions(
|
||||
parsedTags,
|
||||
importPath,
|
||||
appMain,
|
||||
isUsingTsSolutionConfig: isUsingTsSolutionSetup(host),
|
||||
};
|
||||
|
||||
return normalized;
|
||||
|
||||
@ -233,6 +233,8 @@ describe('lib', () => {
|
||||
"compilerOptions": {
|
||||
"outDir": "../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node10",
|
||||
"jsx": "react-jsx",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"files": ["src/test-setup.ts"],
|
||||
|
||||
@ -4,6 +4,7 @@ import {
|
||||
formatFiles,
|
||||
generateFiles,
|
||||
GeneratorCallback,
|
||||
installPackagesTask,
|
||||
joinPathFragments,
|
||||
names,
|
||||
offsetFromRoot,
|
||||
@ -13,6 +14,7 @@ import {
|
||||
Tree,
|
||||
updateJson,
|
||||
updateProjectConfiguration,
|
||||
writeJson,
|
||||
} from '@nx/devkit';
|
||||
|
||||
import {
|
||||
@ -20,7 +22,6 @@ import {
|
||||
getRelativePathToRootTsConfig,
|
||||
initGenerator as jsInitGenerator,
|
||||
} from '@nx/js';
|
||||
import { assertNotUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
import init from '../init/init';
|
||||
import { addLinting } from '../../utils/add-linting';
|
||||
import { addJest } from '../../utils/add-jest';
|
||||
@ -35,6 +36,8 @@ import { ensureDependencies } from '../../utils/ensure-dependencies';
|
||||
import { initRootBabelConfig } from '../../utils/init-root-babel-config';
|
||||
import { addBuildTargetDefaults } from '@nx/devkit/src/generators/target-defaults-utils';
|
||||
import { logShowProjectCommand } from '@nx/devkit/src/utils/log-show-project-command';
|
||||
import { updateTsconfigFiles } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
import { getImportPath } from '@nx/js/src/utils/get-import-path';
|
||||
|
||||
export async function expoLibraryGenerator(
|
||||
host: Tree,
|
||||
@ -50,7 +53,13 @@ export async function expoLibraryGeneratorInternal(
|
||||
host: Tree,
|
||||
schema: Schema
|
||||
): Promise<GeneratorCallback> {
|
||||
assertNotUsingTsSolutionSetup(host, 'expo', 'library');
|
||||
const tasks: GeneratorCallback[] = [];
|
||||
|
||||
const jsInitTask = await jsInitGenerator(host, {
|
||||
...schema,
|
||||
skipFormat: true,
|
||||
});
|
||||
tasks.push(jsInitTask);
|
||||
|
||||
const options = await normalizeOptions(host, schema);
|
||||
if (options.publishable === true && !schema.importPath) {
|
||||
@ -59,13 +68,6 @@ export async function expoLibraryGeneratorInternal(
|
||||
);
|
||||
}
|
||||
|
||||
const tasks: GeneratorCallback[] = [];
|
||||
|
||||
const jsInitTask = await jsInitGenerator(host, {
|
||||
...schema,
|
||||
skipFormat: true,
|
||||
});
|
||||
tasks.push(jsInitTask);
|
||||
const initTask = await init(host, { ...options, skipFormat: true });
|
||||
tasks.push(initTask);
|
||||
if (!options.skipPackageJson) {
|
||||
@ -114,10 +116,29 @@ export async function expoLibraryGeneratorInternal(
|
||||
]);
|
||||
}
|
||||
|
||||
updateTsconfigFiles(
|
||||
host,
|
||||
options.projectRoot,
|
||||
'tsconfig.lib.json',
|
||||
{
|
||||
jsx: 'react-jsx',
|
||||
module: 'esnext',
|
||||
moduleResolution: 'bundler',
|
||||
},
|
||||
options.linter === 'eslint'
|
||||
? ['eslint.config.js', 'eslint.config.cjs', 'eslint.config.mjs']
|
||||
: undefined
|
||||
);
|
||||
|
||||
if (!options.skipFormat) {
|
||||
await formatFiles(host);
|
||||
}
|
||||
|
||||
// Always run install to link packages.
|
||||
if (options.isUsingTsSolutionConfig) {
|
||||
tasks.push(() => installPackagesTask(host));
|
||||
}
|
||||
|
||||
tasks.push(() => {
|
||||
logShowProjectCommand(options.name);
|
||||
});
|
||||
@ -136,7 +157,29 @@ async function addProject(
|
||||
tags: options.parsedTags,
|
||||
targets: {},
|
||||
};
|
||||
|
||||
if (options.isUsingTsSolutionConfig) {
|
||||
const packageName = getImportPath(host, options.name);
|
||||
const sourceEntry = !options.buildable
|
||||
? options.js
|
||||
? './src/index.js'
|
||||
: './src/index.ts'
|
||||
: undefined;
|
||||
writeJson(host, joinPathFragments(options.projectRoot, 'package.json'), {
|
||||
name: packageName,
|
||||
version: '0.0.1',
|
||||
main: sourceEntry,
|
||||
types: sourceEntry,
|
||||
nx: {
|
||||
name: packageName === options.name ? undefined : options.name,
|
||||
projectType: 'library',
|
||||
sourceRoot: joinPathFragments(options.projectRoot, 'src'),
|
||||
tags: options.parsedTags?.length ? options.parsedTags : undefined,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
addProjectConfiguration(host, options.name, project);
|
||||
}
|
||||
|
||||
if (!options.publishable && !options.buildable) {
|
||||
return () => {};
|
||||
|
||||
@ -29,13 +29,16 @@
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "eslint"
|
||||
"default": "none",
|
||||
"x-prompt": "Which linter would you like to use?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"description": "Test runner to use for unit tests.",
|
||||
"default": "jest"
|
||||
"default": "none",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"tags": {
|
||||
"type": "string",
|
||||
|
||||
@ -2,8 +2,9 @@
|
||||
"extends": "<%= extendedConfig %>",
|
||||
"compilerOptions": {
|
||||
"outDir": "<%= outDir %>",<% if (module) { %>
|
||||
"module": "<%= module %>",<% } %>
|
||||
"types": ["jest", "node"]
|
||||
"module": "<%= module %>",<% } if (module === 'commonjs') { %>
|
||||
"moduleResolution": "node10",<% } if (supportTsx) { %>
|
||||
"jsx": "react-jsx",<% } %>"types": ["jest", "node"]
|
||||
},<% if(setupFile !== 'none') { %>
|
||||
"files": ["src/test-setup.ts"],<% } %>
|
||||
"include": [
|
||||
|
||||
@ -4,10 +4,10 @@ import {
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
import { join } from 'path';
|
||||
import type { JestPresetExtension } from '../../../utils/config/config-file';
|
||||
import { NormalizedJestProjectSchema } from '../schema';
|
||||
import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
|
||||
export function createFiles(
|
||||
tree: Tree,
|
||||
|
||||
@ -8,6 +8,7 @@ import {
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
updateJson,
|
||||
writeJson,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { libraryGenerator } from './library';
|
||||
@ -1611,4 +1612,64 @@ describe('lib', () => {
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TS solution setup', () => {
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace();
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
json.workspaces = ['packages/*', 'apps/*'];
|
||||
return json;
|
||||
});
|
||||
writeJson(tree, 'tsconfig.base.json', {
|
||||
compilerOptions: {
|
||||
composite: true,
|
||||
declaration: true,
|
||||
},
|
||||
});
|
||||
writeJson(tree, 'tsconfig.json', {
|
||||
extends: './tsconfig.base.json',
|
||||
files: [],
|
||||
references: [],
|
||||
});
|
||||
});
|
||||
|
||||
it('should map non-buildable libraries to source', async () => {
|
||||
await libraryGenerator(tree, {
|
||||
...defaultOptions,
|
||||
directory: 'my-ts-lib',
|
||||
bundler: 'none',
|
||||
unitTestRunner: 'none',
|
||||
linter: 'none',
|
||||
});
|
||||
await libraryGenerator(tree, {
|
||||
...defaultOptions,
|
||||
directory: 'my-js-lib',
|
||||
js: true,
|
||||
bundler: 'none',
|
||||
unitTestRunner: 'none',
|
||||
linter: 'none',
|
||||
});
|
||||
|
||||
expect(readJson(tree, 'my-ts-lib/package.json')).toMatchInlineSnapshot(`
|
||||
{
|
||||
"dependencies": {},
|
||||
"main": "./src/index.ts",
|
||||
"name": "@proj/my-ts-lib",
|
||||
"private": true,
|
||||
"types": "./src/index.ts",
|
||||
"version": "0.0.1",
|
||||
}
|
||||
`);
|
||||
expect(readJson(tree, 'my-js-lib/package.json')).toMatchInlineSnapshot(`
|
||||
{
|
||||
"dependencies": {},
|
||||
"main": "./src/index.js",
|
||||
"name": "@proj/my-js-lib",
|
||||
"private": true,
|
||||
"types": "./src/index.js",
|
||||
"version": "0.0.1",
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
import {
|
||||
addDependenciesToPackageJson,
|
||||
installPackagesTask,
|
||||
addProjectConfiguration,
|
||||
ensurePackage,
|
||||
formatFiles,
|
||||
@ -234,6 +235,11 @@ export async function libraryGeneratorInternal(
|
||||
});
|
||||
}
|
||||
|
||||
// Always run install to link packages.
|
||||
if (options.isUsingTsSolutionConfig) {
|
||||
tasks.push(() => installPackagesTask(tree));
|
||||
}
|
||||
|
||||
tasks.push(() => {
|
||||
logShowProjectCommand(options.name);
|
||||
});
|
||||
@ -1125,6 +1131,17 @@ function determineEntryFields(
|
||||
// Safest option is to not set a type field.
|
||||
// Allow the user to decide which module format their library is using
|
||||
type: undefined,
|
||||
// For non-buildable libraries, point to source so we can still use them in apps via bundlers like Vite.
|
||||
main: options.isUsingTsSolutionConfig
|
||||
? options.js
|
||||
? './src/index.js'
|
||||
: './src/index.ts'
|
||||
: undefined,
|
||||
types: options.isUsingTsSolutionConfig
|
||||
? options.js
|
||||
? './src/index.js'
|
||||
: './src/index.ts'
|
||||
: undefined,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -268,7 +268,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
|
||||
`);
|
||||
});
|
||||
|
||||
it('should not invoke tsc with `--emitDeclarationOnly` when `noEmit` is set in the tsconfig.json file', async () => {
|
||||
it('should not invoke `tsc --build` when `noEmit` is set in the tsconfig.json file', async () => {
|
||||
// set directly in tsconfig.json file
|
||||
await applyFilesToTempFsAndContext(tempFs, context, {
|
||||
'libs/my-lib/tsconfig.json': JSON.stringify({
|
||||
@ -285,7 +285,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
|
||||
"targets": {
|
||||
"typecheck": {
|
||||
"cache": true,
|
||||
"command": "tsc --build --pretty --verbose",
|
||||
"command": "echo "The 'typecheck' target is disabled because one or more project references set 'noEmit: true' in their tsconfig. Remove this property to resolve this issue."",
|
||||
"dependsOn": [
|
||||
"^typecheck",
|
||||
],
|
||||
@ -345,7 +345,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
|
||||
"targets": {
|
||||
"typecheck": {
|
||||
"cache": true,
|
||||
"command": "tsc --build --pretty --verbose",
|
||||
"command": "echo "The 'typecheck' target is disabled because one or more project references set 'noEmit: true' in their tsconfig. Remove this property to resolve this issue."",
|
||||
"dependsOn": [
|
||||
"^typecheck",
|
||||
],
|
||||
@ -387,7 +387,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
|
||||
`);
|
||||
});
|
||||
|
||||
it('should not invoke tsc with `--emitDeclarationOnly` when `noEmit` is set in any of the referenced tsconfig.json files', async () => {
|
||||
it('should not invoke `tsc --build` when `noEmit` is set in any of the referenced tsconfig.json files', async () => {
|
||||
await applyFilesToTempFsAndContext(tempFs, context, {
|
||||
'libs/my-lib/tsconfig.json': JSON.stringify({
|
||||
files: [],
|
||||
@ -407,7 +407,7 @@ describe(`Plugin: ${PLUGIN_NAME}`, () => {
|
||||
"targets": {
|
||||
"typecheck": {
|
||||
"cache": true,
|
||||
"command": "tsc --build --pretty --verbose",
|
||||
"command": "echo "The 'typecheck' target is disabled because one or more project references set 'noEmit: true' in their tsconfig. Remove this property to resolve this issue."",
|
||||
"dependsOn": [
|
||||
"^typecheck",
|
||||
],
|
||||
|
||||
@ -168,12 +168,23 @@ async function createNodesInternal(
|
||||
return {};
|
||||
}
|
||||
|
||||
// Do not create project for Next.js projects since they are not compatible with
|
||||
// project references and typecheck will fail.
|
||||
if (
|
||||
siblingFiles.includes('next.config.js') ||
|
||||
siblingFiles.includes('next.config.cjs') ||
|
||||
siblingFiles.includes('next.config.mjs') ||
|
||||
siblingFiles.includes('next.config.ts')
|
||||
) {
|
||||
return {};
|
||||
}
|
||||
|
||||
/**
|
||||
* The cache key is composed by:
|
||||
* - hashes of the content of the relevant files that can affect what's inferred by the plugin:
|
||||
* - current config file
|
||||
* - config files extended by the current config file (recursively up to the root config file)
|
||||
* - referenced config files that are internal to the owning Nx project of the current config file
|
||||
* - referenced config files that are internal to the owning Nx project of the current config file, or is a shallow external reference of the owning Nx project
|
||||
* - lock file
|
||||
* - hash of the plugin options
|
||||
* - current config file path
|
||||
@ -185,11 +196,17 @@ async function createNodesInternal(
|
||||
context.workspaceRoot,
|
||||
projectRoot
|
||||
);
|
||||
const externalProjectReferences = resolveShallowExternalProjectReferences(
|
||||
tsConfig,
|
||||
context.workspaceRoot,
|
||||
projectRoot
|
||||
);
|
||||
const nodeHash = hashArray([
|
||||
...[
|
||||
fullConfigPath,
|
||||
...extendedConfigFiles.files,
|
||||
...Object.keys(internalReferencedFiles),
|
||||
...Object.keys(externalProjectReferences),
|
||||
join(context.workspaceRoot, lockFileName),
|
||||
].map(hashFile),
|
||||
hashObject(options),
|
||||
@ -239,6 +256,11 @@ function buildTscTargets(
|
||||
context.workspaceRoot,
|
||||
projectRoot
|
||||
);
|
||||
const externalProjectReferences = resolveShallowExternalProjectReferences(
|
||||
tsConfig,
|
||||
context.workspaceRoot,
|
||||
projectRoot
|
||||
);
|
||||
const targetName = options.typecheck.targetName;
|
||||
if (!targets[targetName]) {
|
||||
let command = `tsc --build --emitDeclarationOnly --pretty --verbose`;
|
||||
@ -246,11 +268,13 @@ function buildTscTargets(
|
||||
tsConfig.options.noEmit ||
|
||||
Object.values(internalProjectReferences).some(
|
||||
(ref) => ref.options.noEmit
|
||||
) ||
|
||||
Object.values(externalProjectReferences).some(
|
||||
(ref) => ref.options.noEmit
|
||||
)
|
||||
) {
|
||||
// `--emitDeclarationOnly` and `--noEmit` are mutually exclusive, so
|
||||
// we remove `--emitDeclarationOnly` if `--noEmit` is set.
|
||||
command = `tsc --build --pretty --verbose`;
|
||||
// `tsc --build` does not work with `noEmit: true`
|
||||
command = `echo "The 'typecheck' target is disabled because one or more project references set 'noEmit: true' in their tsconfig. Remove this property to resolve this issue."`;
|
||||
}
|
||||
|
||||
targets[targetName] = {
|
||||
@ -607,6 +631,48 @@ function resolveInternalProjectReferences(
|
||||
workspaceRoot: string,
|
||||
projectRoot: string,
|
||||
projectReferences: Record<string, ParsedCommandLine> = {}
|
||||
): Record<string, ParsedCommandLine> {
|
||||
walkProjectReferences(
|
||||
tsConfig,
|
||||
workspaceRoot,
|
||||
projectRoot,
|
||||
(configPath, config) => {
|
||||
if (isExternalProjectReference(configPath, workspaceRoot, projectRoot)) {
|
||||
return false;
|
||||
} else {
|
||||
projectReferences[configPath] = config;
|
||||
}
|
||||
}
|
||||
);
|
||||
return projectReferences;
|
||||
}
|
||||
|
||||
function resolveShallowExternalProjectReferences(
|
||||
tsConfig: ParsedCommandLine,
|
||||
workspaceRoot: string,
|
||||
projectRoot: string,
|
||||
projectReferences: Record<string, ParsedCommandLine> = {}
|
||||
): Record<string, ParsedCommandLine> {
|
||||
walkProjectReferences(
|
||||
tsConfig,
|
||||
workspaceRoot,
|
||||
projectRoot,
|
||||
(configPath, config) => {
|
||||
if (isExternalProjectReference(configPath, workspaceRoot, projectRoot)) {
|
||||
projectReferences[configPath] = config;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
);
|
||||
return projectReferences;
|
||||
}
|
||||
|
||||
function walkProjectReferences(
|
||||
tsConfig: ParsedCommandLine,
|
||||
workspaceRoot: string,
|
||||
projectRoot: string,
|
||||
visitor: (configPath: string, config: ParsedCommandLine) => void | false, // false stops recursion
|
||||
projectReferences: Record<string, ParsedCommandLine> = {}
|
||||
): Record<string, ParsedCommandLine> {
|
||||
if (!tsConfig.projectReferences?.length) {
|
||||
return projectReferences;
|
||||
@ -624,22 +690,14 @@ function resolveInternalProjectReferences(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (isExternalProjectReference(refConfigPath, workspaceRoot, projectRoot)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!refConfigPath.endsWith('.json')) {
|
||||
refConfigPath = join(refConfigPath, 'tsconfig.json');
|
||||
}
|
||||
const refTsConfig = readCachedTsConfig(refConfigPath);
|
||||
projectReferences[refConfigPath] = refTsConfig;
|
||||
|
||||
resolveInternalProjectReferences(
|
||||
refTsConfig,
|
||||
workspaceRoot,
|
||||
projectRoot,
|
||||
projectReferences
|
||||
);
|
||||
const result = visitor(refConfigPath, refTsConfig);
|
||||
if (result !== false) {
|
||||
walkProjectReferences(refTsConfig, workspaceRoot, projectRoot, visitor);
|
||||
}
|
||||
}
|
||||
|
||||
return projectReferences;
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import {
|
||||
detectPackageManager,
|
||||
getPackageManagerVersion,
|
||||
isWorkspacesEnabled,
|
||||
output,
|
||||
readJson,
|
||||
type GeneratorCallback,
|
||||
@ -10,6 +9,7 @@ import {
|
||||
import { minimatch } from 'minimatch';
|
||||
import { join } from 'node:path/posix';
|
||||
import { getGlobPatternsFromPackageManagerWorkspaces } from 'nx/src/plugins/package-json';
|
||||
import { PackageJson } from 'nx/src/utils/package-json';
|
||||
import { lt } from 'semver';
|
||||
|
||||
export type ProjectPackageManagerWorkspaceState =
|
||||
@ -37,7 +37,22 @@ export function getProjectPackageManagerWorkspaceState(
|
||||
}
|
||||
|
||||
export function isUsingPackageManagerWorkspaces(tree: Tree): boolean {
|
||||
return isWorkspacesEnabled(detectPackageManager(tree.root), tree.root);
|
||||
return isWorkspacesEnabled(tree);
|
||||
}
|
||||
|
||||
export function isWorkspacesEnabled(
|
||||
tree: Tree
|
||||
// packageManager: PackageManager = detectPackageManager(),
|
||||
// root: string = workspaceRoot
|
||||
): boolean {
|
||||
const packageManager = detectPackageManager(tree.root);
|
||||
if (packageManager === 'pnpm') {
|
||||
return tree.exists('pnpm-workspace.yaml');
|
||||
}
|
||||
|
||||
// yarn and npm both use the same 'workspaces' property in package.json
|
||||
const packageJson = readJson<PackageJson>(tree, 'package.json');
|
||||
return !!packageJson?.workspaces;
|
||||
}
|
||||
|
||||
export function getProjectPackageManagerWorkspaceStateWarningTask(
|
||||
|
||||
@ -1,12 +1,16 @@
|
||||
import {
|
||||
joinPathFragments,
|
||||
offsetFromRoot,
|
||||
output,
|
||||
readJson,
|
||||
readNxJson,
|
||||
workspaceRoot,
|
||||
type Tree,
|
||||
updateJson,
|
||||
workspaceRoot,
|
||||
} from '@nx/devkit';
|
||||
import { FsTree } from 'nx/src/generators/tree';
|
||||
import { isUsingPackageManagerWorkspaces } from '../package-manager-workspaces';
|
||||
import { relative } from 'node:path/posix';
|
||||
|
||||
export function isUsingTypeScriptPlugin(tree: Tree): boolean {
|
||||
const nxJson = readNxJson(tree);
|
||||
@ -96,3 +100,78 @@ export function assertNotUsingTsSolutionSetup(
|
||||
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
export function updateTsconfigFiles(
|
||||
tree: Tree,
|
||||
projectRoot: string,
|
||||
runtimeTsconfigFileName: string,
|
||||
compilerOptions: Record<string, string | boolean | string[]>,
|
||||
exclude: string[] = [],
|
||||
rootDir = 'src'
|
||||
) {
|
||||
if (!isUsingTsSolutionSetup(tree)) return;
|
||||
|
||||
const offset = offsetFromRoot(projectRoot);
|
||||
const tsconfig = `${projectRoot}/${runtimeTsconfigFileName}`;
|
||||
const tsconfigSpec = `${projectRoot}/tsconfig.spec.json`;
|
||||
const e2eRoot = `${projectRoot}-e2e`;
|
||||
const tsconfigE2E = `${e2eRoot}/tsconfig.json`;
|
||||
|
||||
if (tree.exists(tsconfig)) {
|
||||
updateJson(tree, tsconfig, (json) => {
|
||||
json.extends = joinPathFragments(offset, 'tsconfig.base.json');
|
||||
|
||||
json.compilerOptions = {
|
||||
...json.compilerOptions,
|
||||
// Make sure d.ts files from typecheck does not conflict with bundlers.
|
||||
// Other tooling like jest write to "out-tsc/jest" to we just default to "out-tsc/<project-name>".
|
||||
outDir: joinPathFragments('out-tsc', projectRoot.split('/').at(-1)),
|
||||
rootDir,
|
||||
...compilerOptions,
|
||||
};
|
||||
|
||||
const excludeSet: Set<string> = json.exclude
|
||||
? new Set(['dist', ...json.exclude, ...exclude])
|
||||
: new Set(exclude);
|
||||
json.exclude = Array.from(excludeSet);
|
||||
|
||||
return json;
|
||||
});
|
||||
}
|
||||
|
||||
if (tree.exists(tsconfigSpec)) {
|
||||
updateJson(tree, tsconfigSpec, (json) => {
|
||||
json.extends = joinPathFragments(offset, 'tsconfig.base.json');
|
||||
json.compilerOptions = {
|
||||
...json.compilerOptions,
|
||||
...compilerOptions,
|
||||
};
|
||||
const runtimePath = `./${runtimeTsconfigFileName}`;
|
||||
json.references ??= [];
|
||||
if (!json.references.some((x) => x.path === runtimePath))
|
||||
json.references.push({ path: runtimePath });
|
||||
return json;
|
||||
});
|
||||
}
|
||||
|
||||
if (tree.exists(tsconfigE2E)) {
|
||||
// tsconfig.json for e2e projects need to have references array
|
||||
updateJson(tree, tsconfigE2E, (json) => {
|
||||
json.references ??= [];
|
||||
const projectPath = relative(e2eRoot, projectRoot);
|
||||
if (!json.references.some((x) => x.path === projectPath))
|
||||
json.references.push({ path: projectPath });
|
||||
return json;
|
||||
});
|
||||
}
|
||||
|
||||
if (tree.exists('tsconfig.json')) {
|
||||
updateJson(tree, 'tsconfig.json', (json) => {
|
||||
const projectPath = './' + projectRoot;
|
||||
json.references ??= [];
|
||||
if (!json.references.some((x) => x.path === projectPath))
|
||||
json.references.push({ path: projectPath });
|
||||
return json;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,6 +5,8 @@ import {
|
||||
readNxJson,
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
updateJson,
|
||||
writeJson,
|
||||
} from '@nx/devkit';
|
||||
|
||||
import { Schema } from './schema';
|
||||
@ -174,23 +176,22 @@ describe('app', () => {
|
||||
|
||||
describe('--style scss', () => {
|
||||
it('should generate scss styles', async () => {
|
||||
const name = uniq();
|
||||
await applicationGenerator(tree, {
|
||||
directory: name,
|
||||
directory: 'myapp',
|
||||
style: 'scss',
|
||||
});
|
||||
|
||||
expect(tree.exists(`${name}/src/app/page.module.scss`)).toBeTruthy();
|
||||
expect(tree.exists(`${name}/src/app/global.css`)).toBeTruthy();
|
||||
expect(tree.exists(`myapp/src/app/page.module.scss`)).toBeTruthy();
|
||||
expect(tree.exists(`myapp/src/app/global.css`)).toBeTruthy();
|
||||
|
||||
const indexContent = tree.read(`${name}/src/app/page.tsx`, 'utf-8');
|
||||
const indexContent = tree.read(`myapp/src/app/page.tsx`, 'utf-8');
|
||||
expect(indexContent).toContain(`import styles from './page.module.scss'`);
|
||||
expect(tree.read(`${name}/src/app/layout.tsx`, 'utf-8'))
|
||||
expect(tree.read(`myapp/src/app/layout.tsx`, 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"import './global.css';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Welcome to ${name}',
|
||||
title: 'Welcome to myapp',
|
||||
description: 'Generated by create-nx-workspace',
|
||||
};
|
||||
|
||||
@ -212,23 +213,22 @@ describe('app', () => {
|
||||
|
||||
describe('--style less', () => {
|
||||
it('should generate less styles', async () => {
|
||||
const name = uniq();
|
||||
await applicationGenerator(tree, {
|
||||
directory: name,
|
||||
directory: 'myapp',
|
||||
style: 'less',
|
||||
});
|
||||
|
||||
expect(tree.exists(`${name}/src/app/page.module.less`)).toBeTruthy();
|
||||
expect(tree.exists(`${name}/src/app/global.less`)).toBeTruthy();
|
||||
expect(tree.exists(`myapp/src/app/page.module.less`)).toBeTruthy();
|
||||
expect(tree.exists(`myapp/src/app/global.less`)).toBeTruthy();
|
||||
|
||||
const indexContent = tree.read(`${name}/src/app/page.tsx`, 'utf-8');
|
||||
const indexContent = tree.read(`myapp/src/app/page.tsx`, 'utf-8');
|
||||
expect(indexContent).toContain(`import styles from './page.module.less'`);
|
||||
expect(tree.read(`${name}/src/app/layout.tsx`, 'utf-8'))
|
||||
expect(tree.read(`myapp/src/app/layout.tsx`, 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"import './global.less';
|
||||
|
||||
export const metadata = {
|
||||
title: 'Welcome to ${name}',
|
||||
title: 'Welcome to myapp',
|
||||
description: 'Generated by create-nx-workspace',
|
||||
};
|
||||
|
||||
@ -616,10 +616,8 @@ describe('app', () => {
|
||||
});
|
||||
|
||||
it('should add .eslintrc.json and dependencies', async () => {
|
||||
const name = uniq();
|
||||
|
||||
await applicationGenerator(tree, {
|
||||
directory: name,
|
||||
directory: 'myapp',
|
||||
style: 'css',
|
||||
});
|
||||
|
||||
@ -631,7 +629,7 @@ describe('app', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const eslintJson = readJson(tree, `${name}/.eslintrc.json`);
|
||||
const eslintJson = readJson(tree, `myapp/.eslintrc.json`);
|
||||
expect(eslintJson).toMatchInlineSnapshot(`
|
||||
{
|
||||
"extends": [
|
||||
@ -655,7 +653,7 @@ describe('app', () => {
|
||||
"rules": {
|
||||
"@next/next/no-html-link-for-pages": [
|
||||
"error",
|
||||
"${name}/pages",
|
||||
"myapp/pages",
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -838,6 +836,172 @@ describe('app (legacy)', () => {
|
||||
expect(projectConfiguration.targets.build).toBeDefined();
|
||||
expect(projectConfiguration.targets.serve).toBeDefined();
|
||||
});
|
||||
|
||||
describe('TS solution setup', () => {
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace();
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
json.workspaces = ['packages/*', 'apps/*'];
|
||||
return json;
|
||||
});
|
||||
writeJson(tree, 'tsconfig.base.json', {
|
||||
compilerOptions: {
|
||||
composite: true,
|
||||
declaration: true,
|
||||
},
|
||||
});
|
||||
writeJson(tree, 'tsconfig.json', {
|
||||
extends: './tsconfig.base.json',
|
||||
files: [],
|
||||
references: [],
|
||||
});
|
||||
});
|
||||
|
||||
it('should add project references when using TS solution', async () => {
|
||||
await applicationGenerator(tree, {
|
||||
...schema,
|
||||
addPlugin: true,
|
||||
directory: 'myapp',
|
||||
name: 'myapp',
|
||||
});
|
||||
|
||||
expect(readJson(tree, 'tsconfig.json').references).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"path": "./myapp-e2e",
|
||||
},
|
||||
{
|
||||
"path": "./myapp",
|
||||
},
|
||||
]
|
||||
`);
|
||||
expect(readJson(tree, 'myapp/tsconfig.json')).toMatchInlineSnapshot(`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"incremental": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"lib": [
|
||||
"dom",
|
||||
"dom.iterable",
|
||||
"esnext",
|
||||
],
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"noEmit": true,
|
||||
"outDir": "out-tsc/myapp",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"./src/*",
|
||||
],
|
||||
},
|
||||
"plugins": [
|
||||
{
|
||||
"name": "next",
|
||||
},
|
||||
],
|
||||
"resolveJsonModule": true,
|
||||
"rootDir": "src",
|
||||
"strict": true,
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
],
|
||||
},
|
||||
"exclude": [
|
||||
"dist",
|
||||
"node_modules",
|
||||
"jest.config.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.ts",
|
||||
".next",
|
||||
"eslint.config.js",
|
||||
"eslint.config.cjs",
|
||||
"eslint.config.mjs",
|
||||
],
|
||||
"extends": "../tsconfig.base.json",
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.js",
|
||||
"src/**/*.jsx",
|
||||
"../myapp/.next/types/**/*.ts",
|
||||
"../dist/myapp/.next/types/**/*.ts",
|
||||
"next-env.d.ts",
|
||||
],
|
||||
}
|
||||
`);
|
||||
expect(readJson(tree, 'myapp/tsconfig.spec.json')).toMatchInlineSnapshot(`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"jsx": "preserve",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"outDir": "./out-tsc/jest",
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
],
|
||||
},
|
||||
"extends": "../tsconfig.base.json",
|
||||
"include": [
|
||||
"jest.config.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.d.ts",
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.json",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
expect(readJson(tree, 'myapp-e2e/tsconfig.json')).toMatchInlineSnapshot(`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"outDir": "dist",
|
||||
"sourceMap": false,
|
||||
"tsBuildInfoFile": "dist/tsconfig.tsbuildinfo",
|
||||
"types": [
|
||||
"cypress",
|
||||
"node",
|
||||
],
|
||||
},
|
||||
"exclude": [
|
||||
"dist",
|
||||
],
|
||||
"extends": "../tsconfig.base.json",
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.js",
|
||||
"cypress.config.ts",
|
||||
"**/*.cy.ts",
|
||||
"**/*.cy.tsx",
|
||||
"**/*.cy.js",
|
||||
"**/*.cy.jsx",
|
||||
"**/*.d.ts",
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../myapp",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function uniq() {
|
||||
|
||||
@ -7,7 +7,6 @@ import {
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
import { initGenerator as jsInitGenerator } from '@nx/js';
|
||||
import { assertNotUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
import { setupTailwindGenerator } from '@nx/react';
|
||||
import {
|
||||
testingLibraryReactVersion,
|
||||
@ -31,6 +30,7 @@ import { updateCypressTsConfig } from './lib/update-cypress-tsconfig';
|
||||
import { showPossibleWarnings } from './lib/show-possible-warnings';
|
||||
import { tsLibVersion } from '../../utils/versions';
|
||||
import { logShowProjectCommand } from '@nx/devkit/src/utils/log-show-project-command';
|
||||
import { updateTsconfigFiles } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
|
||||
export async function applicationGenerator(host: Tree, schema: Schema) {
|
||||
return await applicationGeneratorInternal(host, {
|
||||
@ -40,8 +40,6 @@ export async function applicationGenerator(host: Tree, schema: Schema) {
|
||||
}
|
||||
|
||||
export async function applicationGeneratorInternal(host: Tree, schema: Schema) {
|
||||
assertNotUsingTsSolutionSetup(host, 'next', 'application');
|
||||
|
||||
const tasks: GeneratorCallback[] = [];
|
||||
const options = await normalizeOptions(host, schema);
|
||||
|
||||
@ -51,6 +49,8 @@ export async function applicationGeneratorInternal(host: Tree, schema: Schema) {
|
||||
js: options.js,
|
||||
skipPackageJson: options.skipPackageJson,
|
||||
skipFormat: true,
|
||||
addTsPlugin: schema.useTsSolution,
|
||||
formatter: schema.formatter,
|
||||
});
|
||||
tasks.push(jsInitTask);
|
||||
|
||||
@ -117,6 +117,21 @@ export async function applicationGeneratorInternal(host: Tree, schema: Schema) {
|
||||
);
|
||||
}
|
||||
|
||||
updateTsconfigFiles(
|
||||
host,
|
||||
options.appProjectRoot,
|
||||
'tsconfig.json',
|
||||
{
|
||||
jsx: 'preserve',
|
||||
module: 'esnext',
|
||||
moduleResolution: 'bundler',
|
||||
},
|
||||
options.linter === 'eslint'
|
||||
? ['.next', 'eslint.config.js', 'eslint.config.cjs', 'eslint.config.mjs']
|
||||
: ['.next'],
|
||||
options.src ? 'src' : '.'
|
||||
);
|
||||
|
||||
if (!options.skipFormat) {
|
||||
await formatFiles(host);
|
||||
}
|
||||
|
||||
@ -3,16 +3,22 @@
|
||||
"compilerOptions": {
|
||||
"jsx": "preserve",
|
||||
<% if (style === '@emotion/styled') { %>"jsxImportSource": "@emotion/react",<% } %>
|
||||
"allowJs": true,
|
||||
"esModuleInterop": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"incremental": true,
|
||||
"plugins": [{ "name": "next" }]
|
||||
"plugins": [{ "name": "next" }]<% if (isUsingTsSolutionSetup) { %>,
|
||||
"paths": {
|
||||
"@/*": [<% if (src) { %>"./src/*"<% } else { %>"./*"<% } %>]
|
||||
}<% } %>
|
||||
},
|
||||
"include": [
|
||||
"<%= rootPath %>**/*.ts",
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
ensurePackage,
|
||||
getPackageManagerCommand,
|
||||
joinPathFragments,
|
||||
readNxJson,
|
||||
Tree,
|
||||
writeJson,
|
||||
} from '@nx/devkit';
|
||||
import { Linter } from '@nx/eslint';
|
||||
|
||||
@ -14,6 +14,7 @@ import { webStaticServeGenerator } from '@nx/web';
|
||||
import { findPluginForConfigFile } from '@nx/devkit/src/utils/find-plugin-for-config-file';
|
||||
import { addE2eCiTargetDefaults } from '@nx/devkit/src/generators/target-defaults-utils';
|
||||
import { getE2EWebServerInfo } from '@nx/devkit/src/generators/e2e-web-server-info-utils';
|
||||
import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
|
||||
export async function addE2e(host: Tree, options: NormalizedSchema) {
|
||||
const nxJson = readNxJson(host);
|
||||
@ -44,13 +45,31 @@ export async function addE2e(host: Tree, options: NormalizedSchema) {
|
||||
});
|
||||
}
|
||||
|
||||
if (isUsingTsSolutionSetup(host)) {
|
||||
writeJson(
|
||||
host,
|
||||
joinPathFragments(options.e2eProjectRoot, 'package.json'),
|
||||
{
|
||||
name: options.e2eProjectName,
|
||||
version: '0.0.1',
|
||||
private: true,
|
||||
nx: {
|
||||
projectType: 'application',
|
||||
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),
|
||||
implicitDependencies: [options.projectName],
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
addProjectConfiguration(host, options.e2eProjectName, {
|
||||
root: options.e2eProjectRoot,
|
||||
projectType: 'application',
|
||||
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),
|
||||
targets: {},
|
||||
tags: [],
|
||||
implicitDependencies: [options.projectName],
|
||||
});
|
||||
}
|
||||
|
||||
const e2eTask = await configurationGenerator(host, {
|
||||
...options,
|
||||
@ -107,13 +126,32 @@ export async function addE2e(host: Tree, options: NormalizedSchema) {
|
||||
const { configurationGenerator } = ensurePackage<
|
||||
typeof import('@nx/playwright')
|
||||
>('@nx/playwright', nxVersion);
|
||||
if (isUsingTsSolutionSetup(host)) {
|
||||
writeJson(
|
||||
host,
|
||||
joinPathFragments(options.e2eProjectRoot, 'package.json'),
|
||||
{
|
||||
name: options.e2eProjectName,
|
||||
version: '0.0.1',
|
||||
private: true,
|
||||
nx: {
|
||||
projectType: 'application',
|
||||
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),
|
||||
implicitDependencies: [options.projectName],
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
addProjectConfiguration(host, options.e2eProjectName, {
|
||||
root: options.e2eProjectRoot,
|
||||
projectType: 'application',
|
||||
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),
|
||||
targets: {},
|
||||
tags: [],
|
||||
implicitDependencies: [options.projectName],
|
||||
});
|
||||
}
|
||||
|
||||
const e2eTask = await configurationGenerator(host, {
|
||||
rootProject: options.rootProject,
|
||||
project: options.e2eProjectName,
|
||||
|
||||
@ -22,6 +22,8 @@ export async function addLinting(
|
||||
host: Tree,
|
||||
options: NormalizedSchema
|
||||
): Promise<GeneratorCallback> {
|
||||
if (options.linter !== 'eslint') return () => {};
|
||||
|
||||
const tasks: GeneratorCallback[] = [];
|
||||
|
||||
tasks.push(
|
||||
|
||||
@ -1,11 +1,17 @@
|
||||
import { NormalizedSchema } from './normalize-options';
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
joinPathFragments,
|
||||
ProjectConfiguration,
|
||||
readNxJson,
|
||||
Tree,
|
||||
writeJson,
|
||||
} from '@nx/devkit';
|
||||
import { addBuildTargetDefaults } from '@nx/devkit/src/generators/target-defaults-utils';
|
||||
import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
import { getImportPath } from '@nx/js/src/utils/get-import-path';
|
||||
import { nextVersion } from '../../../utils/versions';
|
||||
import { reactDomVersion, reactVersion } from '@nx/react';
|
||||
|
||||
export function addProject(host: Tree, options: NormalizedSchema) {
|
||||
const targets: Record<string, any> = {};
|
||||
@ -66,7 +72,26 @@ export function addProject(host: Tree, options: NormalizedSchema) {
|
||||
tags: options.parsedTags,
|
||||
};
|
||||
|
||||
if (isUsingTsSolutionSetup(host)) {
|
||||
writeJson(host, joinPathFragments(options.appProjectRoot, 'package.json'), {
|
||||
name: getImportPath(host, options.name),
|
||||
version: '0.0.1',
|
||||
private: true,
|
||||
dependencies: {
|
||||
next: nextVersion,
|
||||
react: reactVersion,
|
||||
'react-dom': reactDomVersion,
|
||||
},
|
||||
nx: {
|
||||
name: options.name,
|
||||
projectType: 'application',
|
||||
sourceRoot: options.appProjectRoot,
|
||||
tags: options.parsedTags?.length ? options.parsedTags : undefined,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
addProjectConfiguration(host, options.projectName, {
|
||||
...project,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ import {
|
||||
createAppJsx,
|
||||
createStyleRules,
|
||||
} from './create-application-files.helpers';
|
||||
import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
|
||||
export function createApplicationFiles(host: Tree, options: NormalizedSchema) {
|
||||
const offsetFromRoot = _offsetFromRoot(options.appProjectRoot);
|
||||
@ -30,14 +31,15 @@ export function createApplicationFiles(host: Tree, options: NormalizedSchema) {
|
||||
'.next/types/**/*.ts'
|
||||
);
|
||||
|
||||
// scope tsconfig to the project directory so that it doesn't include other projects/libs
|
||||
const rootPath = options.rootProject
|
||||
const rootPath =
|
||||
options.rootProject || isUsingTsSolutionSetup(host)
|
||||
? options.src
|
||||
? 'src/'
|
||||
: options.appDir
|
||||
? 'app/'
|
||||
: 'pages/'
|
||||
: '';
|
||||
|
||||
const templateVariables = {
|
||||
...names(options.name),
|
||||
...options,
|
||||
@ -55,8 +57,8 @@ export function createApplicationFiles(host: Tree, options: NormalizedSchema) {
|
||||
appContent: createAppJsx(options.projectName),
|
||||
styleContent: createStyleRules(),
|
||||
pageStyleContent: `.page {}`,
|
||||
|
||||
stylesExt: options.style === 'less' ? options.style : 'css',
|
||||
isUsingTsSolutionSetup: isUsingTsSolutionSetup(host),
|
||||
};
|
||||
|
||||
const generatedAppFilePath = options.src
|
||||
|
||||
@ -17,6 +17,9 @@ export interface Schema {
|
||||
skipPackageJson?: boolean;
|
||||
appDir?: boolean;
|
||||
src?: boolean;
|
||||
// Internal options
|
||||
rootProject?: boolean;
|
||||
addPlugin?: boolean;
|
||||
useTsSolution?: boolean;
|
||||
formatter?: 'prettier' | 'none';
|
||||
}
|
||||
|
||||
@ -69,8 +69,10 @@
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint"],
|
||||
"default": "eslint"
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "none",
|
||||
"x-prompt": "Which linter would you like to use?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
@ -82,7 +84,9 @@
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"description": "Test runner to use for unit tests.",
|
||||
"default": "jest"
|
||||
"default": "none",
|
||||
"x-prompt": "What unit test runner should be used?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"e2eTestRunner": {
|
||||
"type": "string",
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { Tree } from '@nx/devkit';
|
||||
import { joinPathFragments, Tree } from '@nx/devkit';
|
||||
import {
|
||||
updateJson,
|
||||
generateFiles,
|
||||
@ -11,6 +11,7 @@ import {
|
||||
import { CustomServerSchema } from './schema';
|
||||
import { join } from 'path';
|
||||
import { configureForSwc } from '../../utils/add-swc-to-custom-server';
|
||||
import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
|
||||
export async function customServerGenerator(
|
||||
host: Tree,
|
||||
@ -71,12 +72,18 @@ export async function customServerGenerator(
|
||||
project.root
|
||||
}`;
|
||||
|
||||
const offset = offsetFromRoot(project.root);
|
||||
const isTsSolution = isUsingTsSolutionSetup(host);
|
||||
|
||||
generateFiles(host, join(__dirname, 'files'), project.root, {
|
||||
...options,
|
||||
hasPlugin,
|
||||
projectPathFromDist,
|
||||
offsetFromRoot: offsetFromRoot(project.root),
|
||||
offsetFromRoot: offset,
|
||||
projectRoot: project.root,
|
||||
baseTsConfigPath: isTsSolution
|
||||
? joinPathFragments(offset, 'tsconfig.base.json')
|
||||
: './tsconfig.json',
|
||||
tmpl: '',
|
||||
});
|
||||
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
{
|
||||
"extends": "./tsconfig.json",
|
||||
"extends": "<%= baseTsConfigPath %>",
|
||||
"compilerOptions": {
|
||||
"module": "commonjs",
|
||||
"module": "nodenext",
|
||||
"moduleResolution": "nodenext",
|
||||
"noEmit": false,
|
||||
"incremental": true,
|
||||
<% if(hasPlugin && compiler === 'tsc') { %>
|
||||
|
||||
@ -8,7 +8,6 @@ import {
|
||||
createProjectGraphAsync,
|
||||
} from '@nx/devkit';
|
||||
import { addPlugin } from '@nx/devkit/src/utils/add-plugin';
|
||||
import { assertNotUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
import { reactDomVersion, reactVersion } from '@nx/react/src/utils/versions';
|
||||
import { addGitIgnoreEntry } from '../../utils/add-gitignore-entry';
|
||||
import { nextVersion, nxVersion } from '../../utils/versions';
|
||||
@ -46,8 +45,6 @@ export async function nextInitGeneratorInternal(
|
||||
host: Tree,
|
||||
schema: InitSchema
|
||||
) {
|
||||
assertNotUsingTsSolutionSetup(host, 'next', 'init');
|
||||
|
||||
const nxJson = readNxJson(host);
|
||||
const addPluginDefault =
|
||||
process.env.NX_ADD_PLUGINS !== 'false' &&
|
||||
|
||||
@ -9,13 +9,13 @@ import {
|
||||
} from '@nx/devkit';
|
||||
import { libraryGenerator as reactLibraryGenerator } from '@nx/react/src/generators/library/library';
|
||||
import { addTsConfigPath, initGenerator as jsInitGenerator } from '@nx/js';
|
||||
import { assertNotUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
import { testingLibraryReactVersion } from '@nx/react/src/utils/versions';
|
||||
|
||||
import { nextInitGenerator } from '../init/init';
|
||||
import { Schema } from './schema';
|
||||
import { normalizeOptions } from './lib/normalize-options';
|
||||
import { eslintConfigNextVersion, tsLibVersion } from '../../utils/versions';
|
||||
import { updateTsconfigFiles } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
|
||||
export async function libraryGenerator(host: Tree, rawOptions: Schema) {
|
||||
return await libraryGeneratorInternal(host, {
|
||||
@ -25,8 +25,6 @@ export async function libraryGenerator(host: Tree, rawOptions: Schema) {
|
||||
}
|
||||
|
||||
export async function libraryGeneratorInternal(host: Tree, rawOptions: Schema) {
|
||||
assertNotUsingTsSolutionSetup(host, 'next', 'library');
|
||||
|
||||
const options = await normalizeOptions(host, rawOptions);
|
||||
const tasks: GeneratorCallback[] = [];
|
||||
|
||||
@ -45,7 +43,7 @@ export async function libraryGeneratorInternal(host: Tree, rawOptions: Schema) {
|
||||
|
||||
const libTask = await reactLibraryGenerator(host, {
|
||||
...options,
|
||||
compiler: 'swc',
|
||||
bundler: 'none',
|
||||
skipFormat: true,
|
||||
});
|
||||
tasks.push(libTask);
|
||||
@ -142,6 +140,20 @@ export async function libraryGeneratorInternal(host: Tree, rawOptions: Schema) {
|
||||
}
|
||||
);
|
||||
|
||||
updateTsconfigFiles(
|
||||
host,
|
||||
options.projectRoot,
|
||||
'tsconfig.lib.json',
|
||||
{
|
||||
jsx: 'react-jsx',
|
||||
module: 'esnext',
|
||||
moduleResolution: 'bundler',
|
||||
},
|
||||
options.linter === 'eslint'
|
||||
? ['eslint.config.js', 'eslint.config.cjs', 'eslint.config.mjs']
|
||||
: undefined
|
||||
);
|
||||
|
||||
if (!options.skipFormat) {
|
||||
await formatFiles(host);
|
||||
}
|
||||
|
||||
@ -14,7 +14,9 @@ export interface Schema {
|
||||
linter: Linter | LinterType;
|
||||
component?: boolean;
|
||||
publishable?: boolean;
|
||||
/** @deprecated Use bundler instead. */
|
||||
buildable?: boolean;
|
||||
bundler?: 'none' | 'vite' | 'rollup';
|
||||
importPath?: string;
|
||||
js?: boolean;
|
||||
globalCss?: boolean;
|
||||
|
||||
@ -62,17 +62,29 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"bundler": {
|
||||
"type": "string",
|
||||
"description": "The bundler to use. Choosing 'none' means this library is not buildable.",
|
||||
"enum": ["none", "vite", "rollup"],
|
||||
"default": "none",
|
||||
"x-prompt": "Which bundler would you like to use to build the library? Choose 'none' to skip build setup.",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"linter": {
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint"],
|
||||
"default": "eslint"
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "none",
|
||||
"x-prompt": "Which linter would you like to use?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["vitest", "jest", "none"],
|
||||
"description": "Test runner to use for unit tests.",
|
||||
"default": "vitest"
|
||||
"default": "none",
|
||||
"x-prompt": "What unit test runner should be used?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"tags": {
|
||||
"type": "string",
|
||||
@ -105,7 +117,8 @@
|
||||
"buildable": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Generate a buildable library."
|
||||
"description": "Generate a buildable library that uses rollup to bundle.",
|
||||
"x-deprecated": "Use the `bundler` option for greater control (none, vite, rollup)."
|
||||
},
|
||||
"importPath": {
|
||||
"type": "string",
|
||||
|
||||
@ -95,12 +95,17 @@ export async function configurationGeneratorInternal(
|
||||
};
|
||||
|
||||
if (isTsSolutionSetup) {
|
||||
// skip eslint from typechecking since it extends from root file that is outside rootDir
|
||||
if (options.linter === 'eslint') {
|
||||
tsconfig.exclude = ['dist', 'eslint.config.js'];
|
||||
}
|
||||
|
||||
tsconfig.compilerOptions.outDir = 'dist';
|
||||
tsconfig.compilerOptions.tsBuildInfoFile = 'dist/tsconfig.tsbuildinfo';
|
||||
|
||||
if (!options.rootProject) {
|
||||
// add the project tsconfog to the workspace root tsconfig.json references
|
||||
updateJson(tree, 'tsconfig.json', (json) => {
|
||||
// add the project tsconfig to the workspace root tsconfig.json references
|
||||
json.references ??= [];
|
||||
json.references.push({ path: './' + projectConfig.root });
|
||||
return json;
|
||||
@ -130,6 +135,9 @@ export async function configurationGeneratorInternal(
|
||||
name: importPath,
|
||||
version: '0.0.1',
|
||||
private: true,
|
||||
nx: {
|
||||
name: options.project,
|
||||
},
|
||||
};
|
||||
writeJson(tree, packageJsonPath, packageJson);
|
||||
}
|
||||
|
||||
@ -5,6 +5,8 @@ import {
|
||||
getProjects,
|
||||
readJson,
|
||||
readProjectConfiguration,
|
||||
updateJson,
|
||||
writeJson,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { Linter } from '@nx/eslint';
|
||||
@ -249,4 +251,152 @@ describe('app', () => {
|
||||
expect(readJson(appTree, 'package.json')).toEqual(packageJsonBefore);
|
||||
});
|
||||
});
|
||||
|
||||
describe('TS solution setup', () => {
|
||||
it('should add project references when using TS solution', async () => {
|
||||
const tree = createTreeWithEmptyWorkspace();
|
||||
tree.write('.gitignore', '');
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
json.workspaces = ['packages/*', 'apps/*'];
|
||||
return json;
|
||||
});
|
||||
writeJson(tree, 'tsconfig.base.json', {
|
||||
compilerOptions: {
|
||||
composite: true,
|
||||
declaration: true,
|
||||
},
|
||||
});
|
||||
writeJson(tree, 'tsconfig.json', {
|
||||
extends: './tsconfig.base.json',
|
||||
files: [],
|
||||
references: [],
|
||||
});
|
||||
|
||||
await reactNativeApplicationGenerator(tree, {
|
||||
directory: 'my-app',
|
||||
displayName: 'myApp',
|
||||
tags: 'one,two',
|
||||
linter: Linter.EsLint,
|
||||
e2eTestRunner: 'none',
|
||||
install: false,
|
||||
unitTestRunner: 'jest',
|
||||
bundler: 'vite',
|
||||
addPlugin: true,
|
||||
});
|
||||
|
||||
expect(readJson(tree, 'tsconfig.json').references).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"path": "./my-app",
|
||||
},
|
||||
]
|
||||
`);
|
||||
expect(readJson(tree, 'my-app/tsconfig.json')).toMatchInlineSnapshot(`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"declaration": true,
|
||||
"jsx": "react-native",
|
||||
"lib": [
|
||||
"dom",
|
||||
"esnext",
|
||||
],
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"skipLibCheck": true,
|
||||
},
|
||||
"extends": "../tsconfig.base.json",
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json",
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
expect(readJson(tree, 'my-app/tsconfig.app.json')).toMatchInlineSnapshot(`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"lib": [
|
||||
"dom",
|
||||
],
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"noUnusedLocals": false,
|
||||
"outDir": "out-tsc/my-app",
|
||||
"rootDir": "src",
|
||||
"types": [
|
||||
"node",
|
||||
],
|
||||
},
|
||||
"exclude": [
|
||||
"dist",
|
||||
"jest.config.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/test-setup.ts",
|
||||
"src/**/*.test.ts",
|
||||
"eslint.config.js",
|
||||
"eslint.config.cjs",
|
||||
"eslint.config.mjs",
|
||||
],
|
||||
"extends": "../tsconfig.base.json",
|
||||
"files": [
|
||||
"../node_modules/@nx/react-native/typings/svg.d.ts",
|
||||
],
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
"src/**/*.js",
|
||||
"src/**/*.jsx",
|
||||
],
|
||||
}
|
||||
`);
|
||||
expect(readJson(tree, 'my-app/tsconfig.spec.json'))
|
||||
.toMatchInlineSnapshot(`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"lib": [
|
||||
"dom",
|
||||
],
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"noUnusedLocals": false,
|
||||
"outDir": "./out-tsc/jest",
|
||||
"types": [
|
||||
"jest",
|
||||
"node",
|
||||
],
|
||||
},
|
||||
"extends": "../tsconfig.base.json",
|
||||
"files": [
|
||||
"src/test-setup.ts",
|
||||
],
|
||||
"include": [
|
||||
"jest.config.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.d.ts",
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -9,7 +9,6 @@ import {
|
||||
} from '@nx/devkit';
|
||||
import { initGenerator as jsInitGenerator } from '@nx/js';
|
||||
import { logShowProjectCommand } from '@nx/devkit/src/utils/log-show-project-command';
|
||||
import { assertNotUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
|
||||
import { addLinting } from '../../utils/add-linting';
|
||||
import { addJest } from '../../utils/add-jest';
|
||||
@ -26,6 +25,7 @@ import { Schema } from './schema';
|
||||
import { ensureDependencies } from '../../utils/ensure-dependencies';
|
||||
import { syncDeps } from '../../executors/sync-deps/sync-deps.impl';
|
||||
import { PackageJson } from 'nx/src/utils/package-json';
|
||||
import { updateTsconfigFiles } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
|
||||
export async function reactNativeApplicationGenerator(
|
||||
host: Tree,
|
||||
@ -41,16 +41,16 @@ export async function reactNativeApplicationGeneratorInternal(
|
||||
host: Tree,
|
||||
schema: Schema
|
||||
): Promise<GeneratorCallback> {
|
||||
assertNotUsingTsSolutionSetup(host, 'react-native', 'application');
|
||||
|
||||
const options = await normalizeOptions(host, schema);
|
||||
|
||||
const tasks: GeneratorCallback[] = [];
|
||||
const jsInitTask = await jsInitGenerator(host, {
|
||||
...schema,
|
||||
skipFormat: true,
|
||||
addTsPlugin: schema.useTsSolution,
|
||||
formatter: schema.formatter,
|
||||
});
|
||||
tasks.push(jsInitTask);
|
||||
|
||||
const options = await normalizeOptions(host, schema);
|
||||
const initTask = await initGenerator(host, { ...options, skipFormat: true });
|
||||
tasks.push(initTask);
|
||||
|
||||
@ -127,6 +127,22 @@ export async function reactNativeApplicationGeneratorInternal(
|
||||
});
|
||||
}
|
||||
|
||||
updateTsconfigFiles(
|
||||
host,
|
||||
options.appProjectRoot,
|
||||
'tsconfig.app.json',
|
||||
{
|
||||
jsx: 'react-jsx',
|
||||
module: 'esnext',
|
||||
moduleResolution: 'bundler',
|
||||
noUnusedLocals: false,
|
||||
lib: ['dom'],
|
||||
},
|
||||
options.linter === 'eslint'
|
||||
? ['eslint.config.js', 'eslint.config.cjs', 'eslint.config.mjs']
|
||||
: undefined
|
||||
);
|
||||
|
||||
if (!options.skipFormat) {
|
||||
await formatFiles(host);
|
||||
}
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
joinPathFragments,
|
||||
ProjectConfiguration,
|
||||
readNxJson,
|
||||
TargetConfiguration,
|
||||
Tree,
|
||||
writeJson,
|
||||
} from '@nx/devkit';
|
||||
import { NormalizedSchema } from './normalize-options';
|
||||
import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
import { getImportPath } from '@nx/js/src/utils/get-import-path';
|
||||
|
||||
export function addProject(host: Tree, options: NormalizedSchema) {
|
||||
const nxJson = readNxJson(host);
|
||||
@ -23,10 +27,25 @@ export function addProject(host: Tree, options: NormalizedSchema) {
|
||||
tags: options.parsedTags,
|
||||
};
|
||||
|
||||
if (isUsingTsSolutionSetup(host)) {
|
||||
writeJson(host, joinPathFragments(options.appProjectRoot, 'package.json'), {
|
||||
name: getImportPath(host, options.name),
|
||||
version: '0.0.1',
|
||||
private: true,
|
||||
nx: {
|
||||
name: options.name,
|
||||
projectType: 'application',
|
||||
sourceRoot: `${options.appProjectRoot}/src`,
|
||||
targets: hasPlugin ? {} : getTargets(options),
|
||||
tags: options.parsedTags?.length ? options.parsedTags : undefined,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
addProjectConfiguration(host, options.projectName, {
|
||||
...project,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function getTargets(options: NormalizedSchema) {
|
||||
const architect: { [key: string]: TargetConfiguration } = {};
|
||||
|
||||
@ -16,6 +16,9 @@ export interface Schema {
|
||||
bundler: 'webpack' | 'vite'; // default is webpack
|
||||
install: boolean; // default is true
|
||||
skipPackageJson?: boolean; //default is false
|
||||
// Internal options
|
||||
addPlugin?: boolean;
|
||||
nxCloudToken?: string;
|
||||
useTsSolution?: boolean;
|
||||
formatter?: 'prettier' | 'none';
|
||||
}
|
||||
|
||||
@ -44,13 +44,15 @@
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "eslint"
|
||||
"default": "none",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"description": "Test runner to use for unit tests",
|
||||
"default": "jest"
|
||||
"default": "none",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"tags": {
|
||||
"type": "string",
|
||||
@ -71,7 +73,8 @@
|
||||
"description": "Adds the specified e2e test runner.",
|
||||
"type": "string",
|
||||
"enum": ["playwright", "cypress", "detox", "none"],
|
||||
"default": "playwright"
|
||||
"default": "none",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"install": {
|
||||
"type": "boolean",
|
||||
|
||||
@ -9,7 +9,6 @@ import {
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
import { addPluginV1 } from '@nx/devkit/src/utils/add-plugin';
|
||||
import { assertNotUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
import { createNodes } from '../../../plugins/plugin';
|
||||
import {
|
||||
nxVersion,
|
||||
@ -31,8 +30,6 @@ export async function reactNativeInitGeneratorInternal(
|
||||
host: Tree,
|
||||
schema: Schema
|
||||
) {
|
||||
assertNotUsingTsSolutionSetup(host, 'react-native', 'init');
|
||||
|
||||
addGitIgnoreEntry(host);
|
||||
|
||||
const nxJson = readNxJson(host);
|
||||
|
||||
@ -4,6 +4,7 @@ import {
|
||||
ensureProjectName,
|
||||
} from '@nx/devkit/src/generators/project-name-and-root-utils';
|
||||
import { Schema } from '../schema';
|
||||
import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
|
||||
export interface NormalizedSchema extends Schema {
|
||||
name: string;
|
||||
@ -13,6 +14,7 @@ export interface NormalizedSchema extends Schema {
|
||||
parsedTags: string[];
|
||||
appMain?: string;
|
||||
appSourceRoot?: string;
|
||||
isUsingTsSolutionConfig: boolean;
|
||||
}
|
||||
|
||||
export async function normalizeOptions(
|
||||
@ -50,6 +52,7 @@ export async function normalizeOptions(
|
||||
projectRoot,
|
||||
parsedTags,
|
||||
importPath,
|
||||
isUsingTsSolutionConfig: isUsingTsSolutionSetup(host),
|
||||
};
|
||||
|
||||
return normalized;
|
||||
|
||||
@ -227,6 +227,8 @@ describe('lib', () => {
|
||||
"compilerOptions": {
|
||||
"outDir": "../dist/out-tsc",
|
||||
"module": "commonjs",
|
||||
"moduleResolution": "node10",
|
||||
"jsx": "react-jsx",
|
||||
"types": ["jest", "node"]
|
||||
},
|
||||
"files": ["src/test-setup.ts"],
|
||||
|
||||
@ -4,6 +4,7 @@ import {
|
||||
formatFiles,
|
||||
generateFiles,
|
||||
GeneratorCallback,
|
||||
installPackagesTask,
|
||||
joinPathFragments,
|
||||
names,
|
||||
offsetFromRoot,
|
||||
@ -13,6 +14,7 @@ import {
|
||||
Tree,
|
||||
updateJson,
|
||||
updateProjectConfiguration,
|
||||
writeJson,
|
||||
} from '@nx/devkit';
|
||||
|
||||
import {
|
||||
@ -32,7 +34,11 @@ import { NormalizedSchema, normalizeOptions } from './lib/normalize-options';
|
||||
import { Schema } from './schema';
|
||||
import { ensureDependencies } from '../../utils/ensure-dependencies';
|
||||
import { logShowProjectCommand } from '@nx/devkit/src/utils/log-show-project-command';
|
||||
import { assertNotUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
import {
|
||||
isUsingTsSolutionSetup,
|
||||
updateTsconfigFiles,
|
||||
} from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
import { getImportPath } from '@nx/js/src/utils/get-import-path';
|
||||
|
||||
export async function reactNativeLibraryGenerator(
|
||||
host: Tree,
|
||||
@ -48,7 +54,13 @@ export async function reactNativeLibraryGeneratorInternal(
|
||||
host: Tree,
|
||||
schema: Schema
|
||||
): Promise<GeneratorCallback> {
|
||||
assertNotUsingTsSolutionSetup(host, 'react-native', 'library');
|
||||
const tasks: GeneratorCallback[] = [];
|
||||
|
||||
const jsInitTask = await jsInitGenerator(host, {
|
||||
...schema,
|
||||
skipFormat: true,
|
||||
});
|
||||
tasks.push(jsInitTask);
|
||||
|
||||
const options = await normalizeOptions(host, schema);
|
||||
if (options.publishable === true && !schema.importPath) {
|
||||
@ -57,13 +69,6 @@ export async function reactNativeLibraryGeneratorInternal(
|
||||
);
|
||||
}
|
||||
|
||||
const tasks: GeneratorCallback[] = [];
|
||||
|
||||
const jsInitTask = await jsInitGenerator(host, {
|
||||
...schema,
|
||||
skipFormat: true,
|
||||
});
|
||||
tasks.push(jsInitTask);
|
||||
const initTask = await init(host, { ...options, skipFormat: true });
|
||||
tasks.push(initTask);
|
||||
|
||||
@ -111,11 +116,29 @@ export async function reactNativeLibraryGeneratorInternal(
|
||||
),
|
||||
]);
|
||||
}
|
||||
updateTsconfigFiles(
|
||||
host,
|
||||
options.projectRoot,
|
||||
'tsconfig.lib.json',
|
||||
{
|
||||
jsx: 'react-jsx',
|
||||
module: 'esnext',
|
||||
moduleResolution: 'bundler',
|
||||
},
|
||||
options.linter === 'eslint'
|
||||
? ['eslint.config.js', 'eslint.config.cjs', 'eslint.config.mjs']
|
||||
: undefined
|
||||
);
|
||||
|
||||
if (!options.skipFormat) {
|
||||
await formatFiles(host);
|
||||
}
|
||||
|
||||
// Always run install to link packages.
|
||||
if (options.isUsingTsSolutionConfig) {
|
||||
tasks.push(() => installPackagesTask(host));
|
||||
}
|
||||
|
||||
tasks.push(() => {
|
||||
logShowProjectCommand(options.name);
|
||||
});
|
||||
@ -135,7 +158,27 @@ async function addProject(
|
||||
targets: {},
|
||||
};
|
||||
|
||||
if (options.isUsingTsSolutionConfig) {
|
||||
const sourceEntry = !options.buildable
|
||||
? options.js
|
||||
? './src/index.js'
|
||||
: './src/index.ts'
|
||||
: undefined;
|
||||
writeJson(host, joinPathFragments(options.projectRoot, 'package.json'), {
|
||||
name: getImportPath(host, options.name),
|
||||
version: '0.0.1',
|
||||
main: sourceEntry,
|
||||
types: sourceEntry,
|
||||
nx: {
|
||||
name: options.name,
|
||||
sourceRoot: joinPathFragments(options.projectRoot, 'src'),
|
||||
projectType: 'library',
|
||||
tags: options.parsedTags?.length ? options.parsedTags : undefined,
|
||||
},
|
||||
});
|
||||
} else {
|
||||
addProjectConfiguration(host, options.name, project);
|
||||
}
|
||||
|
||||
if (!options.publishable && !options.buildable) {
|
||||
return () => {};
|
||||
|
||||
@ -32,13 +32,16 @@
|
||||
"description": "The tool to use for running lint checks.",
|
||||
"type": "string",
|
||||
"enum": ["eslint", "none"],
|
||||
"default": "eslint"
|
||||
"default": "none",
|
||||
"x-prompt": "Which linter would you like to use?",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"unitTestRunner": {
|
||||
"type": "string",
|
||||
"enum": ["jest", "none"],
|
||||
"description": "Test runner to use for unit tests.",
|
||||
"default": "jest"
|
||||
"default": "none",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"tags": {
|
||||
"type": "string",
|
||||
|
||||
@ -12,7 +12,11 @@ import {
|
||||
} from '@nx/devkit';
|
||||
import { hasWebpackPlugin } from '@nx/react/src/utils/has-webpack-plugin';
|
||||
|
||||
import { nxVersion, reactNativeWebVersion } from '../../utils/versions';
|
||||
import {
|
||||
nxVersion,
|
||||
reactNativeWebVersion,
|
||||
typesReactDomVersion,
|
||||
} from '../../utils/versions';
|
||||
import { NormalizedSchema, normalizeSchema } from './lib/normalize-schema';
|
||||
import {
|
||||
createBuildTarget,
|
||||
@ -77,6 +81,18 @@ export async function webConfigurationGenerator(
|
||||
);
|
||||
}
|
||||
|
||||
if (!options.skipPackageJson) {
|
||||
tasks.push(
|
||||
addDependenciesToPackageJson(
|
||||
tree,
|
||||
{},
|
||||
{
|
||||
'@types/react-dom': typesReactDomVersion,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (!options.skipFormat) {
|
||||
await formatFiles(tree);
|
||||
}
|
||||
@ -103,6 +119,7 @@ async function addBundlerConfiguration(
|
||||
project: normalizedSchema.project,
|
||||
newProject: true,
|
||||
includeVitest: false,
|
||||
projectType: 'application',
|
||||
compiler: 'babel',
|
||||
skipFormat: true,
|
||||
});
|
||||
|
||||
@ -14,6 +14,7 @@ export const reactVersion = '18.2.0';
|
||||
export const reactDomVersion = '18.2.0';
|
||||
export const reactTestRendererVersion = '18.2.0';
|
||||
export const typesReactVersion = '~18.2.45';
|
||||
export const typesReactDomVersion = '18.3.0';
|
||||
|
||||
export const testingLibraryReactNativeVersion = '~12.5.0';
|
||||
export const testingLibraryJestNativeVersion = '~5.4.3';
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`app --minimal should create default application without Nx welcome component 1`] = `
|
||||
"// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import styles from './app.module.css';
|
||||
"// Uncomment this line to use CSS modules
|
||||
// import styles from './app.module.css';
|
||||
|
||||
export function App() {
|
||||
return (
|
||||
@ -239,8 +239,8 @@ export default defineConfig({
|
||||
`;
|
||||
|
||||
exports[`app not nested should generate files 1`] = `
|
||||
"// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import styles from './app.module.css';
|
||||
"// Uncomment this line to use CSS modules
|
||||
// import styles from './app.module.css';
|
||||
import NxWelcome from './nx-welcome';
|
||||
|
||||
export function App() {
|
||||
@ -343,8 +343,8 @@ module.exports = {
|
||||
`;
|
||||
|
||||
exports[`app should create Nx specific template 1`] = `
|
||||
"// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import styles from './app.module.css';
|
||||
"// Uncomment this line to use CSS modules
|
||||
// import styles from './app.module.css';
|
||||
import NxWelcome from "./nx-welcome";
|
||||
|
||||
export function App() {
|
||||
|
||||
@ -7,7 +7,9 @@ import {
|
||||
readJson,
|
||||
readNxJson,
|
||||
Tree,
|
||||
updateJson,
|
||||
updateNxJson,
|
||||
writeJson,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { Linter } from '@nx/eslint';
|
||||
@ -276,7 +278,6 @@ describe('app', () => {
|
||||
expect(tsconfigApp.compilerOptions.outDir).toEqual('../dist/out-tsc');
|
||||
expect(tsconfigApp.extends).toEqual('./tsconfig.json');
|
||||
expect(tsconfigApp.exclude).toEqual([
|
||||
'jest.config.ts',
|
||||
'src/**/*.spec.ts',
|
||||
'src/**/*.test.ts',
|
||||
'src/**/*.spec.tsx',
|
||||
@ -285,6 +286,7 @@ describe('app', () => {
|
||||
'src/**/*.test.js',
|
||||
'src/**/*.spec.jsx',
|
||||
'src/**/*.test.jsx',
|
||||
'jest.config.ts',
|
||||
]);
|
||||
|
||||
const eslintJson = readJson(appTree, 'my-app/.eslintrc.json');
|
||||
@ -414,7 +416,6 @@ describe('app', () => {
|
||||
path: 'my-dir/my-app/tsconfig.app.json',
|
||||
lookupFn: (json) => json.exclude,
|
||||
expectedValue: [
|
||||
'jest.config.ts',
|
||||
'src/**/*.spec.ts',
|
||||
'src/**/*.test.ts',
|
||||
'src/**/*.spec.tsx',
|
||||
@ -423,6 +424,7 @@ describe('app', () => {
|
||||
'src/**/*.test.js',
|
||||
'src/**/*.spec.jsx',
|
||||
'src/**/*.test.jsx',
|
||||
'jest.config.ts',
|
||||
],
|
||||
},
|
||||
{
|
||||
@ -1241,4 +1243,180 @@ describe('app', () => {
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
describe('TS solution setup', () => {
|
||||
beforeEach(() => {
|
||||
appTree = createTreeWithEmptyWorkspace();
|
||||
updateJson(appTree, 'package.json', (json) => {
|
||||
json.workspaces = ['packages/*', 'apps/*'];
|
||||
return json;
|
||||
});
|
||||
writeJson(appTree, 'tsconfig.base.json', {
|
||||
compilerOptions: {
|
||||
composite: true,
|
||||
declaration: true,
|
||||
},
|
||||
});
|
||||
writeJson(appTree, 'tsconfig.json', {
|
||||
extends: './tsconfig.base.json',
|
||||
files: [],
|
||||
references: [],
|
||||
});
|
||||
});
|
||||
|
||||
it('should add project references when using TS solution', async () => {
|
||||
await applicationGenerator(appTree, {
|
||||
directory: 'myapp',
|
||||
addPlugin: true,
|
||||
linter: Linter.EsLint,
|
||||
style: 'none',
|
||||
bundler: 'vite',
|
||||
unitTestRunner: 'vitest',
|
||||
e2eTestRunner: 'playwright',
|
||||
});
|
||||
|
||||
expect(readJson(appTree, 'tsconfig.json').references)
|
||||
.toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"path": "./myapp-e2e",
|
||||
},
|
||||
{
|
||||
"path": "./myapp",
|
||||
},
|
||||
]
|
||||
`);
|
||||
expect(readJson(appTree, 'myapp/tsconfig.json')).toMatchInlineSnapshot(`
|
||||
{
|
||||
"extends": "../tsconfig.base.json",
|
||||
"files": [],
|
||||
"include": [],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json",
|
||||
},
|
||||
{
|
||||
"path": "./tsconfig.spec.json",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
expect(readJson(appTree, 'myapp/tsconfig.app.json'))
|
||||
.toMatchInlineSnapshot(`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"lib": [
|
||||
"dom",
|
||||
],
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"outDir": "out-tsc/myapp",
|
||||
"rootDir": "src",
|
||||
"tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo",
|
||||
"types": [
|
||||
"node",
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts",
|
||||
"vite/client",
|
||||
],
|
||||
},
|
||||
"exclude": [
|
||||
"dist",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.test.jsx",
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
],
|
||||
"extends": "../tsconfig.base.json",
|
||||
"include": [
|
||||
"src/**/*.js",
|
||||
"src/**/*.jsx",
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx",
|
||||
],
|
||||
}
|
||||
`);
|
||||
expect(readJson(appTree, 'myapp/tsconfig.spec.json'))
|
||||
.toMatchInlineSnapshot(`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"jsx": "react-jsx",
|
||||
"module": "esnext",
|
||||
"moduleResolution": "bundler",
|
||||
"outDir": "./out-tsc/vitest",
|
||||
"types": [
|
||||
"vitest/globals",
|
||||
"vitest/importMeta",
|
||||
"vite/client",
|
||||
"node",
|
||||
"vitest",
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts",
|
||||
],
|
||||
},
|
||||
"extends": "../tsconfig.base.json",
|
||||
"include": [
|
||||
"vite.config.ts",
|
||||
"vite.config.mts",
|
||||
"vitest.config.ts",
|
||||
"vitest.config.mts",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.test.tsx",
|
||||
"src/**/*.spec.tsx",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.jsx",
|
||||
"src/**/*.spec.jsx",
|
||||
"src/**/*.d.ts",
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "./tsconfig.app.json",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
expect(readJson(appTree, 'myapp-e2e/tsconfig.json'))
|
||||
.toMatchInlineSnapshot(`
|
||||
{
|
||||
"compilerOptions": {
|
||||
"allowJs": true,
|
||||
"outDir": "dist",
|
||||
"sourceMap": false,
|
||||
"tsBuildInfoFile": "dist/tsconfig.tsbuildinfo",
|
||||
},
|
||||
"exclude": [
|
||||
"dist",
|
||||
"eslint.config.js",
|
||||
],
|
||||
"extends": "../tsconfig.base.json",
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"**/*.js",
|
||||
"playwright.config.ts",
|
||||
"src/**/*.spec.ts",
|
||||
"src/**/*.spec.js",
|
||||
"src/**/*.test.ts",
|
||||
"src/**/*.test.js",
|
||||
"src/**/*.d.ts",
|
||||
],
|
||||
"references": [
|
||||
{
|
||||
"path": "../myapp",
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -21,14 +21,9 @@ import {
|
||||
Tree,
|
||||
updateNxJson,
|
||||
} from '@nx/devkit';
|
||||
|
||||
import reactInitGenerator from '../init/init';
|
||||
import { Linter, lintProjectGenerator } from '@nx/eslint';
|
||||
import {
|
||||
babelLoaderVersion,
|
||||
nxRspackVersion,
|
||||
nxVersion,
|
||||
} from '../../utils/versions';
|
||||
import { babelLoaderVersion, nxVersion } from '../../utils/versions';
|
||||
import { maybeJs } from '../../utils/maybe-js';
|
||||
import { installCommonDependencies } from './lib/install-common-dependencies';
|
||||
import { extractTsConfigBase } from '../../utils/create-ts-config';
|
||||
@ -46,7 +41,7 @@ import { initGenerator as jsInitGenerator } from '@nx/js';
|
||||
import { logShowProjectCommand } from '@nx/devkit/src/utils/log-show-project-command';
|
||||
import { setupTailwindGenerator } from '../setup-tailwind/setup-tailwind';
|
||||
import { useFlatConfig } from '@nx/eslint/src/utils/flat-config';
|
||||
import { assertNotUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
import { updateTsconfigFiles } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
|
||||
async function addLinting(host: Tree, options: NormalizedSchema) {
|
||||
const tasks: GeneratorCallback[] = [];
|
||||
@ -114,20 +109,20 @@ export async function applicationGeneratorInternal(
|
||||
host: Tree,
|
||||
schema: Schema
|
||||
): Promise<GeneratorCallback> {
|
||||
assertNotUsingTsSolutionSetup(host, 'react', 'application');
|
||||
|
||||
const tasks = [];
|
||||
|
||||
const options = await normalizeOptions(host, schema);
|
||||
showPossibleWarnings(host, options);
|
||||
|
||||
const jsInitTask = await jsInitGenerator(host, {
|
||||
...schema,
|
||||
tsConfigName: schema.rootProject ? 'tsconfig.json' : 'tsconfig.base.json',
|
||||
skipFormat: true,
|
||||
addTsPlugin: schema.useTsSolution,
|
||||
formatter: schema.formatter,
|
||||
});
|
||||
tasks.push(jsInitTask);
|
||||
|
||||
const options = await normalizeOptions(host, schema);
|
||||
showPossibleWarnings(host, options);
|
||||
|
||||
const initTask = await reactInitGenerator(host, {
|
||||
...options,
|
||||
skipFormat: true,
|
||||
@ -165,10 +160,7 @@ export async function applicationGeneratorInternal(
|
||||
tasks.push(ensureDependencies(host, { uiFramework: 'react' }));
|
||||
}
|
||||
} else if (options.bundler === 'rspack') {
|
||||
const { rspackInitGenerator } = ensurePackage(
|
||||
'@nx/rspack',
|
||||
nxRspackVersion
|
||||
);
|
||||
const { rspackInitGenerator } = ensurePackage('@nx/rspack', nxVersion);
|
||||
const rspackInitTask = await rspackInitGenerator(host, {
|
||||
...options,
|
||||
addPlugin: false,
|
||||
@ -213,6 +205,7 @@ export async function applicationGeneratorInternal(
|
||||
compiler: options.compiler,
|
||||
skipFormat: true,
|
||||
addPlugin: options.addPlugin,
|
||||
projectType: 'application',
|
||||
});
|
||||
tasks.push(viteTask);
|
||||
createOrEditViteConfig(
|
||||
@ -236,6 +229,26 @@ export async function applicationGeneratorInternal(
|
||||
},
|
||||
false
|
||||
);
|
||||
} else if (options.bundler === 'rspack') {
|
||||
const { configurationGenerator } = ensurePackage('@nx/rspack', nxVersion);
|
||||
const rspackTask = await configurationGenerator(host, {
|
||||
project: options.projectName,
|
||||
main: joinPathFragments(
|
||||
options.appProjectRoot,
|
||||
maybeJs(
|
||||
{
|
||||
js: options.js,
|
||||
useJsx: true,
|
||||
},
|
||||
`src/main.tsx`
|
||||
)
|
||||
),
|
||||
tsConfig: joinPathFragments(options.appProjectRoot, 'tsconfig.app.json'),
|
||||
target: 'web',
|
||||
newProject: true,
|
||||
framework: 'react',
|
||||
});
|
||||
tasks.push(rspackTask);
|
||||
}
|
||||
|
||||
if (options.bundler !== 'vite' && options.unitTestRunner === 'vitest') {
|
||||
@ -348,6 +361,12 @@ export async function applicationGeneratorInternal(
|
||||
);
|
||||
}
|
||||
|
||||
updateTsconfigFiles(host, options.appProjectRoot, 'tsconfig.app.json', {
|
||||
jsx: 'react-jsx',
|
||||
module: 'esnext',
|
||||
moduleResolution: 'bundler',
|
||||
});
|
||||
|
||||
if (!options.skipFormat) {
|
||||
await formatFiles(host);
|
||||
}
|
||||
|
||||
@ -1,14 +1,31 @@
|
||||
{
|
||||
<%_ if (isUsingTsSolutionSetup) { _%>{
|
||||
"extends": "<%= offsetFromRoot%>tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo",
|
||||
"jsx": "react-jsx",
|
||||
"lib": ["dom"],
|
||||
"types": [
|
||||
"node",
|
||||
<%_ if (style === 'styled-jsx') { _%>"@nx/react/typings/styled-jsx.d.ts",<%_ } _%>
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts"
|
||||
]
|
||||
},
|
||||
"exclude": ["src/**/*.spec.ts", "src/**/*.test.ts", "src/**/*.spec.tsx", "src/**/*.test.tsx", "src/**/*.spec.js", "src/**/*.test.js", "src/**/*.spec.jsx", "src/**/*.test.jsx"],
|
||||
"include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"]
|
||||
}<% } else { %>{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "<%= offsetFromRoot %>dist/out-tsc",
|
||||
"types": [
|
||||
"node",
|
||||
<%_ if (style === 'styled-jsx') { %>"@nx/react/typings/styled-jsx.d.ts",<% } _%>
|
||||
<%_ if (style === 'styled-jsx') { _%>"@nx/react/typings/styled-jsx.d.ts",<%_ } _%>
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts"
|
||||
]
|
||||
},
|
||||
"exclude": ["jest.config.ts","src/**/*.spec.ts", "src/**/*.test.ts", "src/**/*.spec.tsx", "src/**/*.test.tsx", "src/**/*.spec.js", "src/**/*.test.js", "src/**/*.spec.jsx", "src/**/*.test.jsx"],
|
||||
"exclude": ["src/**/*.spec.ts", "src/**/*.test.ts", "src/**/*.spec.tsx", "src/**/*.test.tsx", "src/**/*.spec.js", "src/**/*.test.js", "src/**/*.spec.jsx", "src/**/*.test.jsx"],
|
||||
"include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"]
|
||||
}
|
||||
<% } %>
|
||||
|
||||
@ -1,4 +1,20 @@
|
||||
{
|
||||
<%_ if (isUsingTsSolutionSetup) { _%>{
|
||||
"extends": "<%= offsetFromRoot%>tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo",
|
||||
"jsx": "react-jsx",
|
||||
"lib": ["dom"],
|
||||
"types": [
|
||||
"node",
|
||||
<%_ if (style === 'styled-jsx') { _%>"@nx/react/typings/styled-jsx.d.ts",<%_ } _%>
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts"
|
||||
]
|
||||
},
|
||||
"exclude": ["src/**/*.spec.ts", "src/**/*.test.ts", "src/**/*.spec.tsx", "src/**/*.test.tsx", "src/**/*.spec.js", "src/**/*.test.js", "src/**/*.spec.jsx", "src/**/*.test.jsx"],
|
||||
"include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"]
|
||||
}<% } else { %>{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "<%= offsetFromRoot %>dist/out-tsc",
|
||||
@ -12,3 +28,4 @@
|
||||
"exclude": ["src/**/*.spec.ts", "src/**/*.test.ts", "src/**/*.spec.tsx", "src/**/*.test.tsx", "src/**/*.spec.js", "src/**/*.test.js", "src/**/*.spec.jsx", "src/**/*.test.jsx"],
|
||||
"include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"]
|
||||
}
|
||||
<% } %>
|
||||
|
||||
@ -1,4 +1,20 @@
|
||||
{
|
||||
<%_ if (isUsingTsSolutionSetup) { _%>{
|
||||
"extends": "<%= offsetFromRoot%>tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "dist",
|
||||
"tsBuildInfoFile": "dist/tsconfig.lib.tsbuildinfo",
|
||||
"jsx": "react-jsx",
|
||||
"lib": ["dom"],
|
||||
"types": [
|
||||
"node",
|
||||
<%_ if (style === 'styled-jsx') { _%>"@nx/react/typings/styled-jsx.d.ts",<%_ } _%>
|
||||
"@nx/react/typings/cssmodule.d.ts",
|
||||
"@nx/react/typings/image.d.ts"
|
||||
]
|
||||
},
|
||||
"exclude": ["src/**/*.spec.ts", "src/**/*.test.ts", "src/**/*.spec.tsx", "src/**/*.test.tsx", "src/**/*.spec.js", "src/**/*.test.js", "src/**/*.spec.jsx", "src/**/*.test.jsx"],
|
||||
"include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"]
|
||||
}<% } else { %>{
|
||||
"extends": "./tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "<%= offsetFromRoot %>dist/out-tsc",
|
||||
@ -9,6 +25,7 @@
|
||||
"@nx/react/typings/image.d.ts"
|
||||
]
|
||||
},
|
||||
"exclude": ["jest.config.ts","src/**/*.spec.ts", "src/**/*.test.ts", "src/**/*.spec.tsx", "src/**/*.test.tsx", "src/**/*.spec.js", "src/**/*.test.js", "src/**/*.spec.jsx", "src/**/*.test.jsx"],
|
||||
"exclude": ["src/**/*.spec.ts", "src/**/*.test.ts", "src/**/*.spec.tsx", "src/**/*.test.tsx", "src/**/*.spec.js", "src/**/*.test.js", "src/**/*.spec.jsx", "src/**/*.test.jsx"],
|
||||
"include": ["src/**/*.js", "src/**/*.jsx", "src/**/*.ts", "src/**/*.tsx"]
|
||||
}
|
||||
<% } %>
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
<% if (classComponent) { %>
|
||||
import { Component } from 'react';
|
||||
<%_ } _%>
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
import styles from './<%= fileName %>.module.<%= style %>';
|
||||
// Uncomment this line to use CSS modules
|
||||
// import styles from './<%= fileName %>.module.<%= style %>';
|
||||
<%_ if (!minimal) { _%>
|
||||
import NxWelcome from "./nx-welcome";
|
||||
<%_ } _%>
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
import type { GeneratorCallback, Tree } from '@nx/devkit';
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
ensurePackage,
|
||||
GeneratorCallback,
|
||||
getPackageManagerCommand,
|
||||
joinPathFragments,
|
||||
readNxJson,
|
||||
Tree,
|
||||
writeJson,
|
||||
} from '@nx/devkit';
|
||||
import { webStaticServeGenerator } from '@nx/web';
|
||||
|
||||
@ -81,6 +83,22 @@ export async function addE2e(
|
||||
typeof import('@nx/cypress')
|
||||
>('@nx/cypress', nxVersion);
|
||||
|
||||
if (options.isUsingTsSolutionConfig) {
|
||||
writeJson(
|
||||
tree,
|
||||
joinPathFragments(options.e2eProjectRoot, 'package.json'),
|
||||
{
|
||||
name: options.e2eProjectName,
|
||||
version: '0.0.1',
|
||||
private: true,
|
||||
nx: {
|
||||
projectType: 'application',
|
||||
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),
|
||||
implicitDependencies: [options.projectName],
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
addProjectConfiguration(tree, options.e2eProjectName, {
|
||||
projectType: 'application',
|
||||
root: options.e2eProjectRoot,
|
||||
@ -89,6 +107,7 @@ export async function addE2e(
|
||||
implicitDependencies: [options.projectName],
|
||||
tags: [],
|
||||
});
|
||||
}
|
||||
|
||||
const e2eTask = await configurationGenerator(tree, {
|
||||
...options,
|
||||
@ -157,6 +176,22 @@ export async function addE2e(
|
||||
const { configurationGenerator } = ensurePackage<
|
||||
typeof import('@nx/playwright')
|
||||
>('@nx/playwright', nxVersion);
|
||||
if (options.isUsingTsSolutionConfig) {
|
||||
writeJson(
|
||||
tree,
|
||||
joinPathFragments(options.e2eProjectRoot, 'package.json'),
|
||||
{
|
||||
name: options.e2eProjectName,
|
||||
version: '0.0.1',
|
||||
private: true,
|
||||
nx: {
|
||||
projectType: 'application',
|
||||
sourceRoot: joinPathFragments(options.e2eProjectRoot, 'src'),
|
||||
implicitDependencies: [options.projectName],
|
||||
},
|
||||
}
|
||||
);
|
||||
} else {
|
||||
addProjectConfiguration(tree, options.e2eProjectName, {
|
||||
projectType: 'application',
|
||||
root: options.e2eProjectRoot,
|
||||
@ -164,6 +199,8 @@ export async function addE2e(
|
||||
targets: {},
|
||||
implicitDependencies: [options.projectName],
|
||||
});
|
||||
}
|
||||
|
||||
const e2eTask = await configurationGenerator(tree, {
|
||||
project: options.e2eProjectName,
|
||||
skipFormat: true,
|
||||
|
||||
@ -23,5 +23,6 @@ export async function addJest(
|
||||
setupFile: 'none',
|
||||
compiler: options.compiler,
|
||||
skipFormat: true,
|
||||
runtimeTsconfigFileName: 'tsconfig.app.json',
|
||||
});
|
||||
}
|
||||
|
||||
@ -5,10 +5,12 @@ import {
|
||||
ProjectConfiguration,
|
||||
TargetConfiguration,
|
||||
Tree,
|
||||
writeJson,
|
||||
} from '@nx/devkit';
|
||||
import { hasWebpackPlugin } from '../../../utils/has-webpack-plugin';
|
||||
import { maybeJs } from '../../../utils/maybe-js';
|
||||
import { hasRspackPlugin } from '../../../utils/has-rspack-plugin';
|
||||
import { getImportPath } from '@nx/js/src/utils/get-import-path';
|
||||
|
||||
export function addProject(host: Tree, options: NormalizedSchema) {
|
||||
const project: ProjectConfiguration = {
|
||||
@ -36,10 +38,26 @@ export function addProject(host: Tree, options: NormalizedSchema) {
|
||||
};
|
||||
}
|
||||
|
||||
if (options.isUsingTsSolutionConfig) {
|
||||
writeJson(host, joinPathFragments(options.appProjectRoot, 'package.json'), {
|
||||
name: getImportPath(host, options.name),
|
||||
version: '0.0.1',
|
||||
private: true,
|
||||
nx: {
|
||||
name: options.name,
|
||||
projectType: 'application',
|
||||
sourceRoot: `${options.appProjectRoot}/src`,
|
||||
tags: options.parsedTags?.length ? options.parsedTags : undefined,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
if (!options.isUsingTsSolutionConfig || options.alwaysGenerateProjectJson) {
|
||||
addProjectConfiguration(host, options.projectName, {
|
||||
...project,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function createRspackBuildTarget(
|
||||
options: NormalizedSchema
|
||||
|
||||
@ -18,10 +18,11 @@ import { hasWebpackPlugin } from '../../../utils/has-webpack-plugin';
|
||||
import { NormalizedSchema } from '../schema';
|
||||
import { getAppTests } from './get-app-tests';
|
||||
import {
|
||||
getNxCloudAppOnBoardingUrl,
|
||||
createNxCloudOnboardingURLForWelcomeApp,
|
||||
getNxCloudAppOnBoardingUrl,
|
||||
} from 'nx/src/nx-cloud/utilities/onboarding';
|
||||
import { hasRspackPlugin } from '../../../utils/has-rspack-plugin';
|
||||
import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
|
||||
export async function createApplicationFiles(
|
||||
host: Tree,
|
||||
@ -67,6 +68,7 @@ export async function createApplicationFiles(
|
||||
inSourceVitestTests: getInSourceVitestTestsTemplate(appTests),
|
||||
style: options.style === 'tailwind' ? 'css' : options.style,
|
||||
hasStyleFile,
|
||||
isUsingTsSolutionSetup: isUsingTsSolutionSetup(host),
|
||||
};
|
||||
|
||||
if (options.bundler === 'vite') {
|
||||
|
||||
@ -6,6 +6,7 @@ import {
|
||||
import { assertValidStyle } from '../../../utils/assertion';
|
||||
import { NormalizedSchema, Schema } from '../schema';
|
||||
import { findFreePort } from './find-free-port';
|
||||
import { isUsingTsSolutionSetup } from '@nx/js/src/utils/typescript/ts-solution-setup';
|
||||
|
||||
export function normalizeDirectory(options: Schema) {
|
||||
options.directory = options.directory?.replace(/\\{1,2}/g, '/');
|
||||
@ -67,6 +68,7 @@ export async function normalizeOptions<T extends Schema = Schema>(
|
||||
fileName,
|
||||
styledModule,
|
||||
hasStyles: options.style !== 'none',
|
||||
isUsingTsSolutionConfig: isUsingTsSolutionSetup(host),
|
||||
} as NormalizedSchema;
|
||||
|
||||
normalized.routing = normalized.routing ?? false;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user