feat(core): cleanup for v19 (#22993)
This commit is contained in:
parent
307f8d4624
commit
a64a7e2db9
@ -136,7 +136,6 @@ rust-toolchain @nrwl/nx-native-reviewers
|
||||
/packages/devkit/** @nrwl/nx-devkit-reviewers
|
||||
/packages/devkit/index.ts @FrozenPandaz @vsavkin
|
||||
/packages/devkit/public-api.ts @FrozenPandaz @vsavkin
|
||||
/packages/devkit/nx.ts @FrozenPandaz @vsavkin
|
||||
|
||||
# Gradle
|
||||
/packages/gradle/** @FrozenPandaz @xiongemi
|
||||
|
||||
@ -19,7 +19,6 @@ It only uses language primitives and immutable objects
|
||||
### Classes
|
||||
|
||||
- [ProjectGraphBuilder](../../devkit/documents/ProjectGraphBuilder)
|
||||
- [Workspaces](../../devkit/documents/Workspaces)
|
||||
|
||||
### Interfaces
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
|
||||
**`Deprecated`**
|
||||
|
||||
use hashTask(task:Task, taskGraph: TaskGraph, env: NodeJS.ProcessEnv) instead. This will be removed in v19
|
||||
use hashTask(task:Task, taskGraph: TaskGraph, env: NodeJS.ProcessEnv) instead. This will be removed in v20
|
||||
|
||||
▸ **hashTask**(`task`, `taskGraph`): `Promise`\<[`Hash`](../../devkit/documents/Hash)\>
|
||||
|
||||
@ -42,7 +42,7 @@ use hashTask(task:Task, taskGraph: TaskGraph, env: NodeJS.ProcessEnv) instead. T
|
||||
|
||||
**`Deprecated`**
|
||||
|
||||
use hashTask(task:Task, taskGraph: TaskGraph, env: NodeJS.ProcessEnv) instead. This will be removed in v19
|
||||
use hashTask(task:Task, taskGraph: TaskGraph, env: NodeJS.ProcessEnv) instead. This will be removed in v20
|
||||
|
||||
▸ **hashTask**(`task`, `taskGraph`, `env`): `Promise`\<[`Hash`](../../devkit/documents/Hash)\>
|
||||
|
||||
@ -76,7 +76,7 @@ use hashTask(task:Task, taskGraph: TaskGraph, env: NodeJS.ProcessEnv) instead. T
|
||||
|
||||
**`Deprecated`**
|
||||
|
||||
use hashTasks(tasks:Task[], taskGraph: TaskGraph, env: NodeJS.ProcessEnv) instead. This will be removed in v19
|
||||
use hashTasks(tasks:Task[], taskGraph: TaskGraph, env: NodeJS.ProcessEnv) instead. This will be removed in v20
|
||||
|
||||
▸ **hashTasks**(`tasks`, `taskGraph`): `Promise`\<[`Hash`](../../devkit/documents/Hash)[]\>
|
||||
|
||||
@ -93,7 +93,7 @@ use hashTasks(tasks:Task[], taskGraph: TaskGraph, env: NodeJS.ProcessEnv) instea
|
||||
|
||||
**`Deprecated`**
|
||||
|
||||
use hashTasks(tasks:Task[], taskGraph: TaskGraph, env: NodeJS.ProcessEnv) instead. This will be removed in v19
|
||||
use hashTasks(tasks:Task[], taskGraph: TaskGraph, env: NodeJS.ProcessEnv) instead. This will be removed in v20
|
||||
|
||||
▸ **hashTasks**(`tasks`, `taskGraph`, `env`): `Promise`\<[`Hash`](../../devkit/documents/Hash)[]\>
|
||||
|
||||
|
||||
@ -1,45 +0,0 @@
|
||||
# Class: Workspaces
|
||||
|
||||
**`Deprecated`**
|
||||
|
||||
This will be removed in v19. Use [readProjectsConfigurationFromProjectGraph](../../devkit/documents/readProjectsConfigurationFromProjectGraph) instead.
|
||||
|
||||
## Table of contents
|
||||
|
||||
### Constructors
|
||||
|
||||
- [constructor](../../devkit/documents/Workspaces#constructor)
|
||||
|
||||
### Methods
|
||||
|
||||
- [readWorkspaceConfiguration](../../devkit/documents/Workspaces#readworkspaceconfiguration)
|
||||
|
||||
## Constructors
|
||||
|
||||
### constructor
|
||||
|
||||
• **new Workspaces**(`root`): [`Workspaces`](../../devkit/documents/Workspaces)
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type |
|
||||
| :----- | :------- |
|
||||
| `root` | `string` |
|
||||
|
||||
#### Returns
|
||||
|
||||
[`Workspaces`](../../devkit/documents/Workspaces)
|
||||
|
||||
## Methods
|
||||
|
||||
### readWorkspaceConfiguration
|
||||
|
||||
▸ **readWorkspaceConfiguration**(): [`ProjectsConfigurations`](../../devkit/documents/ProjectsConfigurations) & [`NxJsonConfiguration`](../../devkit/documents/NxJsonConfiguration)\<`string`[] \| `"*"`\>
|
||||
|
||||
#### Returns
|
||||
|
||||
[`ProjectsConfigurations`](../../devkit/documents/ProjectsConfigurations) & [`NxJsonConfiguration`](../../devkit/documents/NxJsonConfiguration)\<`string`[] \| `"*"`\>
|
||||
|
||||
**`Deprecated`**
|
||||
|
||||
Use [readProjectsConfigurationFromProjectGraph](../../devkit/documents/readProjectsConfigurationFromProjectGraph) instead.
|
||||
@ -8630,23 +8630,6 @@
|
||||
"isExternal": false,
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"id": "executors",
|
||||
"path": "/nx-api/plugin/executors",
|
||||
"name": "executors",
|
||||
"children": [
|
||||
{
|
||||
"id": "e2e",
|
||||
"path": "/nx-api/plugin/executors/e2e",
|
||||
"name": "e2e",
|
||||
"children": [],
|
||||
"isExternal": false,
|
||||
"disableCollapsible": false
|
||||
}
|
||||
],
|
||||
"isExternal": false,
|
||||
"disableCollapsible": false
|
||||
},
|
||||
{
|
||||
"id": "generators",
|
||||
"path": "/nx-api/plugin/generators",
|
||||
|
||||
@ -2072,17 +2072,7 @@
|
||||
},
|
||||
"root": "/packages/plugin",
|
||||
"source": "/packages/plugin/src",
|
||||
"executors": {
|
||||
"/nx-api/plugin/executors/e2e": {
|
||||
"description": "Creates and runs the E2E tests for an Nx Plugin.",
|
||||
"file": "generated/packages/plugin/executors/e2e.json",
|
||||
"hidden": false,
|
||||
"name": "e2e",
|
||||
"originalFilePath": "/packages/plugin/src/executors/e2e/schema.json",
|
||||
"path": "/nx-api/plugin/executors/e2e",
|
||||
"type": "executor"
|
||||
}
|
||||
},
|
||||
"executors": {},
|
||||
"generators": {
|
||||
"/nx-api/plugin/generators/plugin": {
|
||||
"description": "Create a Nx Plugin.",
|
||||
|
||||
@ -2047,17 +2047,7 @@
|
||||
"originalFilePath": "shared/packages/plugin/plugin"
|
||||
}
|
||||
],
|
||||
"executors": [
|
||||
{
|
||||
"description": "Creates and runs the E2E tests for an Nx Plugin.",
|
||||
"file": "generated/packages/plugin/executors/e2e.json",
|
||||
"hidden": false,
|
||||
"name": "e2e",
|
||||
"originalFilePath": "/packages/plugin/src/executors/e2e/schema.json",
|
||||
"path": "plugin/executors/e2e",
|
||||
"type": "executor"
|
||||
}
|
||||
],
|
||||
"executors": [],
|
||||
"generators": [
|
||||
{
|
||||
"description": "Create a Nx Plugin.",
|
||||
|
||||
@ -19,7 +19,6 @@ It only uses language primitives and immutable objects
|
||||
### Classes
|
||||
|
||||
- [ProjectGraphBuilder](../../devkit/documents/ProjectGraphBuilder)
|
||||
- [Workspaces](../../devkit/documents/Workspaces)
|
||||
|
||||
### Interfaces
|
||||
|
||||
|
||||
@ -1,209 +0,0 @@
|
||||
{
|
||||
"name": "e2e",
|
||||
"implementation": "/packages/plugin/src/executors/e2e/e2e.impl.ts",
|
||||
"schema": {
|
||||
"version": 2,
|
||||
"outputCapture": "direct-nodejs",
|
||||
"title": "Nx Plugin Playground Target",
|
||||
"description": "Creates a playground for a Nx Plugin.",
|
||||
"cli": "nx",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"target": {
|
||||
"description": "The build target for the Nx Plugin project.",
|
||||
"type": "string",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"jestConfig": {
|
||||
"type": "string",
|
||||
"description": "Jest config file.",
|
||||
"x-completion-type": "file",
|
||||
"x-completion-glob": "jest.config@(.js|.ts)",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"tsSpecConfig": {
|
||||
"type": "string",
|
||||
"description": "The tsconfig file for specs.",
|
||||
"x-deprecated": "Use the `tsconfig` property for `ts-jest` in the e2e project `jest.config.js` file. It will be removed in the next major release.",
|
||||
"x-completion-type": "file",
|
||||
"x-completion-glob": "tsconfig.*.json",
|
||||
"x-priority": "important"
|
||||
},
|
||||
"codeCoverage": {
|
||||
"description": "Indicates that test coverage information should be collected and reported in the output. (https://jestjs.io/docs/cli#--coverageboolean)",
|
||||
"type": "boolean",
|
||||
"aliases": ["coverage"]
|
||||
},
|
||||
"config": {
|
||||
"description": "The path to a Jest config file specifying how to find and execute tests. If no `rootDir` is set in the config, the directory containing the config file is assumed to be the `rootDir` for the project. This can also be a JSON-encoded value which Jest will use as configuration.",
|
||||
"type": "string"
|
||||
},
|
||||
"clearCache": {
|
||||
"description": "Deletes the Jest cache directory and then exits without running tests. Will delete Jest's default cache directory. _Note: clearing the cache will reduce performance_.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"detectOpenHandles": {
|
||||
"description": "Attempt to collect and print open handles preventing Jest from exiting cleanly (https://jestjs.io/docs/cli#--detectopenhandles)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"logHeapUsage": {
|
||||
"description": "Logs the heap usage after every test. Useful to debug memory leaks. Use together with --runInBand and --expose-gc in node.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"detectLeaks": {
|
||||
"description": "**EXPERIMENTAL**: Detect memory leaks in tests. After executing a test, it will try to garbage collect the global object used, and fail if it was leaked",
|
||||
"type": "boolean"
|
||||
},
|
||||
"testFile": {
|
||||
"description": "The name of the file to test.",
|
||||
"type": "string",
|
||||
"x-completion-type": "file",
|
||||
"x-completion-glob": "**/*.+(spec|test).+(ts|js)"
|
||||
},
|
||||
"tsConfig": {
|
||||
"description": "The name of the Typescript configuration file. Set the tsconfig option in the jest config file. ",
|
||||
"type": "string",
|
||||
"x-deprecated": "Use the ts-jest configuration options in the jest config file instead."
|
||||
},
|
||||
"setupFile": {
|
||||
"description": "The name of a setup file used by Jest. (use Jest config file https://jestjs.io/docs/en/configuration#setupfilesafterenv-array)",
|
||||
"type": "string",
|
||||
"x-deprecated": "Use the setupFilesAfterEnv option in the jest config file. https://jestjs.io/docs/en/configuration#setupfilesafterenv-array"
|
||||
},
|
||||
"bail": {
|
||||
"alias": "b",
|
||||
"description": "Exit the test suite immediately after `n` number of failing tests. (https://jestjs.io/docs/cli#--bail)",
|
||||
"oneOf": [{ "type": "number" }, { "type": "boolean" }]
|
||||
},
|
||||
"ci": {
|
||||
"description": "Whether to run Jest in continuous integration (CI) mode. This option is on by default in most popular CI environments. It will prevent snapshots from being written unless explicitly requested. (https://jestjs.io/docs/cli#--ci)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"color": {
|
||||
"alias": "colors",
|
||||
"description": "Forces test results output color highlighting (even if `stdout` is not a TTY). Set to false if you would like to have no colors. (https://jestjs.io/docs/cli#--colors)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"findRelatedTests": {
|
||||
"description": "Find and run the tests that cover a comma separated list of source files that were passed in as arguments. (https://jestjs.io/docs/cli#--findrelatedtests-spaceseparatedlistofsourcefiles)",
|
||||
"type": "string"
|
||||
},
|
||||
"json": {
|
||||
"description": "Prints the test results in `JSON`. This mode will send all other test output and user messages to `stderr`. (https://jestjs.io/docs/cli#--json)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"maxWorkers": {
|
||||
"alias": "w",
|
||||
"description": "Specifies the maximum number of workers the worker-pool will spawn for running tests. This defaults to the number of the cores available on your machine. Useful for CI. (its usually best not to override this default) (https://jestjs.io/docs/cli#--maxworkersnumstring)",
|
||||
"oneOf": [{ "type": "number" }, { "type": "string" }]
|
||||
},
|
||||
"onlyChanged": {
|
||||
"alias": "o",
|
||||
"description": "Attempts to identify which tests to run based on which files have changed in the current repository. Only works if you're running tests in a `git` or `hg` repository at the moment. (https://jestjs.io/docs/cli#--onlychanged)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"changedSince": {
|
||||
"description": "Runs tests related to the changes since the provided branch or commit hash. If the current branch has diverged from the given branch, then only changes made locally will be tested. (https://jestjs.io/docs/cli#--changedsince)",
|
||||
"type": "string"
|
||||
},
|
||||
"outputFile": {
|
||||
"description": "Write test results to a file when the `--json` option is also specified. (https://jestjs.io/docs/cli#--outputfilefilename)",
|
||||
"type": "string"
|
||||
},
|
||||
"passWithNoTests": {
|
||||
"description": "Will not fail if no tests are found (for example while using `--testPathPattern`.) (https://jestjs.io/docs/cli#--passwithnotests)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"runInBand": {
|
||||
"alias": "i",
|
||||
"description": "Run all tests serially in the current process (rather than creating a worker pool of child processes that run tests). This is sometimes useful for debugging, but such use cases are pretty rare. Useful for CI. (https://jestjs.io/docs/cli#--runinband)",
|
||||
"type": "boolean",
|
||||
"default": true
|
||||
},
|
||||
"showConfig": {
|
||||
"description": "Print your Jest config and then exits. (https://jestjs.io/docs/en/cli#--showconfig)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"silent": {
|
||||
"description": "Prevent tests from printing messages through the console. (https://jestjs.io/docs/cli#--silent)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"testNamePattern": {
|
||||
"alias": "t",
|
||||
"description": "Run only tests with a name that matches the regex pattern. (https://jestjs.io/docs/cli#--testnamepatternregex)",
|
||||
"type": "string"
|
||||
},
|
||||
"testPathIgnorePatterns": {
|
||||
"description": "An array of regexp pattern strings that is matched against all tests paths before executing the test. Only run those tests with a path that does not match with the provided regexp expressions. (https://jestjs.io/docs/cli#--testpathignorepatternsregexarray)",
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"testPathPattern": {
|
||||
"description": "An array of regexp pattern strings that is matched against all tests paths before executing the test. (https://jestjs.io/docs/cli#--testpathpatternregex)",
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"default": []
|
||||
},
|
||||
"colors": {
|
||||
"description": "Forces test results output highlighting even if stdout is not a TTY. (https://jestjs.io/docs/cli#--colors)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"reporters": {
|
||||
"description": "Run tests with specified reporters. Reporter options are not available via CLI. Example with multiple reporters: `jest --reporters=\"default\" --reporters=\"jest-junit\"`. (https://jestjs.io/docs/cli#--reporters)",
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"verbose": {
|
||||
"description": "Display individual test results with the test suite hierarchy. (https://jestjs.io/docs/cli#--verbose)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"coverageReporters": {
|
||||
"description": "A list of reporter names that Jest uses when writing coverage reports. Any istanbul reporter.",
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
},
|
||||
"coverageDirectory": {
|
||||
"description": "The directory where Jest should output its coverage files.",
|
||||
"type": "string"
|
||||
},
|
||||
"testResultsProcessor": {
|
||||
"description": "Node module that implements a custom results processor. (https://jestjs.io/docs/en/configuration#testresultsprocessor-string)",
|
||||
"type": "string"
|
||||
},
|
||||
"updateSnapshot": {
|
||||
"alias": "u",
|
||||
"description": "Use this flag to re-record snapshots. Can be used together with a test suite pattern or with `--testNamePattern` to re-record snapshot for test matching the pattern. (https://jestjs.io/docs/cli#--updatesnapshot)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"useStderr": {
|
||||
"description": "Divert all output to stderr.",
|
||||
"type": "boolean"
|
||||
},
|
||||
"watch": {
|
||||
"description": "Watch files for changes and rerun tests related to changed files. If you want to re-run all tests when a file has changed, use the `--watchAll` option. (https://jestjs.io/docs/cli#--watch)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"watchAll": {
|
||||
"description": "Watch files for changes and rerun all tests when something changes. If you want to re-run only the tests that depend on the changed files, use the `--watch` option. (https://jestjs.io/docs/cli#--watchall)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"testLocationInResults": {
|
||||
"description": "Adds a location field to test results. Used to report location of a test in a reporter. { \"column\": 4, \"line\": 5 } (https://jestjs.io/docs/cli#--testlocationinresults)",
|
||||
"type": "boolean"
|
||||
},
|
||||
"testTimeout": {
|
||||
"description": "Default timeout of a test in milliseconds. Default value: `5000`. (https://jestjs.io/docs/cli#--testtimeoutnumber)",
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": ["target", "jestConfig"],
|
||||
"presets": []
|
||||
},
|
||||
"description": "Creates and runs the E2E tests for an Nx Plugin.",
|
||||
"x-deprecated": "@nx/plugin:e2e is deprecated and will be removed in Nx v19. Use @nx/jest:jest instead.",
|
||||
"aliases": [],
|
||||
"hidden": false,
|
||||
"path": "/packages/plugin/src/executors/e2e/schema.json",
|
||||
"type": "executor"
|
||||
}
|
||||
@ -562,8 +562,6 @@
|
||||
- [plugin](/nx-api/plugin)
|
||||
- [documents](/nx-api/plugin/documents)
|
||||
- [Overview](/nx-api/plugin/documents/overview)
|
||||
- [executors](/nx-api/plugin/executors)
|
||||
- [e2e](/nx-api/plugin/executors/e2e)
|
||||
- [generators](/nx-api/plugin/generators)
|
||||
- [plugin](/nx-api/plugin/generators/plugin)
|
||||
- [create-package](/nx-api/plugin/generators/create-package)
|
||||
|
||||
@ -275,7 +275,6 @@ const pages: Array<{ title: string; path: string }> = [
|
||||
title: '@nx/plugin:executor',
|
||||
path: '/packages/plugin/generators/executor',
|
||||
},
|
||||
{ title: '@nx/plugin:e2e', path: '/packages/plugin/executors/e2e' },
|
||||
{ title: '@nx/react', path: '/packages/react' },
|
||||
{ title: '@nx/react:init', path: '/packages/react/generators/init' },
|
||||
{
|
||||
|
||||
@ -1,29 +1,5 @@
|
||||
{
|
||||
"generators": {
|
||||
"add-cypress-inputs": {
|
||||
"cli": "nx",
|
||||
"version": "15.0.0-beta.0",
|
||||
"description": "Stop hashing cypress spec files and config files for build targets and dependent tasks",
|
||||
"factory": "./src/migrations/update-15-0-0/add-cypress-inputs"
|
||||
},
|
||||
"update-cy-mount-usage": {
|
||||
"cli": "nx",
|
||||
"version": "15.0.0-beta.4",
|
||||
"description": "Update to using cy.mount in the commands.ts file instead of importing mount for each component test file",
|
||||
"factory": "./src/migrations/update-15-0-0/update-cy-mount-usage"
|
||||
},
|
||||
"update-to-cypress-11": {
|
||||
"cli": "nx",
|
||||
"version": "15.1.0-beta.0",
|
||||
"description": "Update to Cypress v11. This migration will only update if the workspace is already on v10. https://www.cypress.io/blog/2022/11/04/upcoming-changes-to-component-testing/",
|
||||
"factory": "./src/migrations/update-15-1-0/cypress-11"
|
||||
},
|
||||
"update-to-cypress-12": {
|
||||
"cli": "nx",
|
||||
"version": "15.5.0-beta.0",
|
||||
"description": "Update to Cypress v12. Cypress 12 contains a handful of breaking changes that might causes tests to start failing that nx cannot directly fix. Read more Cypress 12 changes: https://docs.cypress.io/guides/references/migration-guide#Migrating-to-Cypress-12-0.This migration will only run if you are already using Cypress v11.",
|
||||
"factory": "./src/migrations/update-15-5-0/update-to-cypress-12"
|
||||
},
|
||||
"update-16-0-0-add-nx-packages": {
|
||||
"cli": "nx",
|
||||
"version": "16.0.0-beta.1",
|
||||
|
||||
@ -1,221 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`update cy.mount usage should add the mount command 1`] = `
|
||||
"import { mount } from 'cypress/angular'
|
||||
/// <reference types="cypress" />
|
||||
|
||||
// ***********************************************
|
||||
// This example commands.ts shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
interface Chainable<Subject> {
|
||||
login(email: string, password: string): void;
|
||||
mount: typeof mount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// -- This is a parent command --
|
||||
Cypress.Commands.add('login', (email, password) => {
|
||||
console.log('Custom command example: Login', email, password);
|
||||
});
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||
|
||||
Cypress.Commands.add('mount', mount);"
|
||||
`;
|
||||
|
||||
exports[`update cy.mount usage should update angular ct test file 1`] = `
|
||||
"
|
||||
import { MountConfig } from 'cypress/angular';
|
||||
|
||||
describe('MyComponent', () => {
|
||||
it('should work', () => {
|
||||
cy.mount(MyComponent);
|
||||
});
|
||||
|
||||
it('should work with config', () => {
|
||||
cy.mount(MyComponent, {...config, componentProperties: {foo: 'bar'}});
|
||||
});
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`update cy.mount usage should update angular react test file 1`] = `
|
||||
"
|
||||
|
||||
|
||||
describe('MyComponent', () => {
|
||||
it('should work', () => {
|
||||
cy.mount(<MyComponent />);
|
||||
});
|
||||
|
||||
it('should work with config', () => {
|
||||
cy.mount(<MyComponent title={"blah"}/>,);
|
||||
});
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`update cy.mount usage should update angular react18 test file 1`] = `
|
||||
"
|
||||
|
||||
|
||||
describe('MyComponent', () => {
|
||||
it('should work', () => {
|
||||
cy.mount(<MyComponent />);
|
||||
});
|
||||
|
||||
it('should work with config', () => {
|
||||
cy.mount(<MyComponent title={"blah"}/>);
|
||||
});
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`update cy.mount usage should work 1`] = `
|
||||
"import { mount } from 'cypress/angular';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
interface Chainable<Subject> {
|
||||
login(email: string, password: string): void;
|
||||
mount: typeof mount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -- This is a parent command --
|
||||
Cypress.Commands.add('login', (email, password) => {
|
||||
console.log('Custom command example: Login', email, password);
|
||||
});
|
||||
Cypress.Commands.add('mount', mount);
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`update cy.mount usage should work 2`] = `
|
||||
"import { MountConfig } from 'cypress/angular';
|
||||
|
||||
describe('MyComponent', () => {
|
||||
it('should work', () => {
|
||||
cy.mount(<MyComponent />);
|
||||
});
|
||||
|
||||
it('should work with config', () => {
|
||||
cy.mount(<MyComponent title={'blah'} />);
|
||||
});
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`update cy.mount usage should work 3`] = `
|
||||
"import { MountConfig } from 'cypress/angular';
|
||||
|
||||
describe('MyComponent', () => {
|
||||
it('should work', () => {
|
||||
cy.mount(<MyComponent />);
|
||||
});
|
||||
|
||||
it('should work with config', () => {
|
||||
cy.mount(<MyComponent title={'blah'} />);
|
||||
});
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`update cy.mount usage should work 4`] = `
|
||||
"import { MountConfig } from 'cypress/angular';
|
||||
|
||||
describe('MyComponent', () => {
|
||||
it('should work', () => {
|
||||
cy.mount(MyComponent);
|
||||
});
|
||||
|
||||
it('should work with config', () => {
|
||||
cy.mount(MyComponent, { ...config, componentProperties: { foo: 'bar' } });
|
||||
});
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`update cy.mount usage should work 5`] = `
|
||||
"// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
declare namespace Cypress {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
interface Chainable<Subject> {
|
||||
login(email: string, password: string): void;
|
||||
mount(c: any): void;
|
||||
}
|
||||
}
|
||||
|
||||
// -- This is a parent command --
|
||||
Cypress.Commands.add('login', (email, password) => {
|
||||
console.log('Custom command example: Login', email, password);
|
||||
});
|
||||
Cypress.Commands.add('mount', (any) => {
|
||||
console.log(mount);
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`update cy.mount usage should work 6`] = `
|
||||
"describe('MyComponent', () => {
|
||||
it('should work', () => {
|
||||
cy.mount(<MyComponent />);
|
||||
});
|
||||
|
||||
it('should work with config', () => {
|
||||
cy.mount(<MyComponent title={'blah'} />);
|
||||
});
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`update cy.mount usage should work 7`] = `
|
||||
"describe('MyComponent', () => {
|
||||
it('should work', () => {
|
||||
cy.mount(<MyComponent />);
|
||||
});
|
||||
|
||||
it('should work with config', () => {
|
||||
cy.mount(<MyComponent title={'blah'} />);
|
||||
});
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`update cy.mount usage should work 8`] = `
|
||||
"describe('MyComponent', () => {
|
||||
it('should work', () => {
|
||||
cy.mount(MyComponent);
|
||||
});
|
||||
|
||||
it('should work with config', () => {
|
||||
cy.mount(MyComponent, { ...config, componentProperties: { foo: 'bar' } });
|
||||
});
|
||||
});
|
||||
"
|
||||
`;
|
||||
@ -1,141 +0,0 @@
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
readNxJson,
|
||||
Tree,
|
||||
updateNxJson,
|
||||
} from '@nx/devkit';
|
||||
import addCypressInputs from './add-cypress-inputs';
|
||||
|
||||
describe('15.0.0 migration (add-cypress-inputs)', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
});
|
||||
|
||||
it('should add inputs configuration for cypress targets', async () => {
|
||||
updateNxJson(tree, {
|
||||
namedInputs: {
|
||||
default: ['{projectRoot}/**/*', 'sharedGlobals'],
|
||||
sharedGlobals: [],
|
||||
production: ['default'],
|
||||
},
|
||||
});
|
||||
addProjectConfiguration(tree, 'proj', {
|
||||
root: 'proj',
|
||||
targets: {
|
||||
e2e: {
|
||||
executor: '@nrwl/cypress:cypress',
|
||||
options: {},
|
||||
},
|
||||
e2e2: {
|
||||
executor: '@nrwl/cypress:cypress',
|
||||
options: {},
|
||||
},
|
||||
notTest: {
|
||||
executor: 'nx:run-commands',
|
||||
},
|
||||
},
|
||||
});
|
||||
tree.write('jest.preset.js', '');
|
||||
|
||||
await addCypressInputs(tree);
|
||||
|
||||
const updated = readNxJson(tree);
|
||||
expect(updated).toMatchInlineSnapshot(`
|
||||
{
|
||||
"namedInputs": {
|
||||
"default": [
|
||||
"{projectRoot}/**/*",
|
||||
"sharedGlobals",
|
||||
],
|
||||
"production": [
|
||||
"default",
|
||||
],
|
||||
"sharedGlobals": [],
|
||||
},
|
||||
"targetDefaults": {
|
||||
"e2e": {
|
||||
"inputs": [
|
||||
"default",
|
||||
"^production",
|
||||
],
|
||||
},
|
||||
"e2e2": {
|
||||
"inputs": [
|
||||
"default",
|
||||
"^production",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should inputs configuration for cypress component testing targets', async () => {
|
||||
updateNxJson(tree, {
|
||||
namedInputs: {
|
||||
default: ['{projectRoot}/**/*', 'sharedGlobals'],
|
||||
sharedGlobals: [],
|
||||
production: ['default'],
|
||||
},
|
||||
});
|
||||
addProjectConfiguration(tree, 'proj', {
|
||||
root: 'proj',
|
||||
targets: {
|
||||
e2e: {
|
||||
executor: '@nrwl/cypress:cypress',
|
||||
options: {
|
||||
testingType: 'component',
|
||||
},
|
||||
},
|
||||
e2e2: {
|
||||
executor: '@nrwl/cypress:cypress',
|
||||
options: {
|
||||
testingType: 'component',
|
||||
},
|
||||
},
|
||||
notTest: {
|
||||
executor: 'nx:run-commands',
|
||||
},
|
||||
},
|
||||
});
|
||||
tree.write('jest.preset.js', '');
|
||||
|
||||
await addCypressInputs(tree);
|
||||
|
||||
const updated = readNxJson(tree);
|
||||
expect(updated).toMatchInlineSnapshot(`
|
||||
{
|
||||
"namedInputs": {
|
||||
"default": [
|
||||
"{projectRoot}/**/*",
|
||||
"sharedGlobals",
|
||||
],
|
||||
"production": [
|
||||
"default",
|
||||
"!{projectRoot}/cypress/**/*",
|
||||
"!{projectRoot}/**/*.cy.[jt]s?(x)",
|
||||
"!{projectRoot}/cypress.config.[jt]s",
|
||||
],
|
||||
"sharedGlobals": [],
|
||||
},
|
||||
"targetDefaults": {
|
||||
"e2e": {
|
||||
"inputs": [
|
||||
"default",
|
||||
"^production",
|
||||
],
|
||||
},
|
||||
"e2e2": {
|
||||
"inputs": [
|
||||
"default",
|
||||
"^production",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
@ -1,52 +0,0 @@
|
||||
import { formatFiles, readNxJson, Tree, updateNxJson } from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { CypressExecutorOptions } from '../../executors/cypress/cypress.impl';
|
||||
|
||||
export default async function (tree: Tree) {
|
||||
const nxJson = readNxJson(tree);
|
||||
|
||||
const { cypressTargets, hasComponentTesting } = getCypressTargetNames(tree);
|
||||
const hasProductionFileset = !!nxJson.namedInputs?.production;
|
||||
|
||||
if (hasComponentTesting && hasProductionFileset && cypressTargets.size > 0) {
|
||||
const productionFileset = new Set(nxJson.namedInputs.production);
|
||||
for (const exclusion of [
|
||||
'!{projectRoot}/cypress/**/*',
|
||||
'!{projectRoot}/**/*.cy.[jt]s?(x)',
|
||||
'!{projectRoot}/cypress.config.[jt]s',
|
||||
]) {
|
||||
productionFileset.add(exclusion);
|
||||
}
|
||||
nxJson.namedInputs.production = Array.from(productionFileset);
|
||||
}
|
||||
|
||||
for (const targetName of cypressTargets) {
|
||||
nxJson.targetDefaults ??= {};
|
||||
const cypressTargetDefaults = (nxJson.targetDefaults[targetName] ??= {});
|
||||
|
||||
cypressTargetDefaults.inputs ??= [
|
||||
'default',
|
||||
hasProductionFileset ? '^production' : '^default',
|
||||
];
|
||||
}
|
||||
|
||||
updateNxJson(tree, nxJson);
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
function getCypressTargetNames(tree: Tree) {
|
||||
const cypressTargets = new Set<string>();
|
||||
let hasComponentTesting = false;
|
||||
forEachExecutorOptions<CypressExecutorOptions>(
|
||||
tree,
|
||||
'@nrwl/cypress:cypress',
|
||||
(options, __, target) => {
|
||||
cypressTargets.add(target);
|
||||
if (options.testingType === 'component') {
|
||||
hasComponentTesting = true;
|
||||
}
|
||||
}
|
||||
);
|
||||
return { cypressTargets, hasComponentTesting };
|
||||
}
|
||||
@ -1,338 +0,0 @@
|
||||
import { installedCypressVersion } from '../../utils/cypress-version';
|
||||
import {
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import {
|
||||
addMountCommand,
|
||||
updateCyFile,
|
||||
updateCyMountUsage,
|
||||
} from './update-cy-mount-usage';
|
||||
import { libraryGenerator } from '@nx/js';
|
||||
import cypressComponentConfiguration from '../../generators/component-configuration/component-configuration';
|
||||
|
||||
jest.mock('../../utils/cypress-version');
|
||||
// nested code imports graph from the repo, which might have innacurate graph version
|
||||
jest.mock('nx/src/project-graph/project-graph', () => ({
|
||||
...jest.requireActual<any>('nx/src/project-graph/project-graph'),
|
||||
createProjectGraphAsync: jest
|
||||
.fn()
|
||||
.mockImplementation(async () => ({ nodes: {}, dependencies: {} })),
|
||||
}));
|
||||
|
||||
describe('update cy.mount usage', () => {
|
||||
let tree: Tree;
|
||||
let mockedInstalledCypressVersion: jest.Mock<
|
||||
ReturnType<typeof installedCypressVersion>
|
||||
> = installedCypressVersion as never;
|
||||
// TODO(@leosvelperez): Turn these tests back to adding the plugin
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
mockedInstalledCypressVersion.mockReturnValue(10);
|
||||
});
|
||||
|
||||
it('should work', async () => {
|
||||
await setup(tree);
|
||||
await updateCyMountUsage(tree);
|
||||
|
||||
expect(
|
||||
tree.read('libs/my-lib/cypress/support/commands.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('libs/my-lib/src/lib/my-cmp-one.cy.js', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('libs/my-lib/src/lib/my-cmp-two.cy.tsx', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('libs/my-lib/src/lib/my-cmp-three.cy.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('libs/another-lib/cypress/support/commands.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('libs/another-lib/src/lib/my-cmp-one.cy.js', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('libs/another-lib/src/lib/my-cmp-two.cy.tsx', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('libs/another-lib/src/lib/my-cmp-three.cy.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should add the mount command', async () => {
|
||||
tree.write(
|
||||
'apps/my-app/cypress/support/commands.ts',
|
||||
`/// <reference types="cypress" />
|
||||
|
||||
// ***********************************************
|
||||
// This example commands.ts shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
interface Chainable<Subject> {
|
||||
login(email: string, password: string): void;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// -- This is a parent command --
|
||||
Cypress.Commands.add('login', (email, password) => {
|
||||
console.log('Custom command example: Login', email, password);
|
||||
});
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
||||
`
|
||||
);
|
||||
addMountCommand(tree, 'apps/my-app', 'angular');
|
||||
expect(
|
||||
tree.read('apps/my-app/cypress/support/commands.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should update angular ct test file', () => {
|
||||
tree.write(
|
||||
'my-file.cy.ts',
|
||||
`
|
||||
import { MountConfig, mount } from 'cypress/angular';
|
||||
|
||||
describe('MyComponent', () => {
|
||||
it('should work', () => {
|
||||
mount(MyComponent);
|
||||
});
|
||||
|
||||
it('should work with config', () => {
|
||||
mount(MyComponent, {...config, componentProperties: {foo: 'bar'}});
|
||||
});
|
||||
});
|
||||
`
|
||||
);
|
||||
updateCyFile(tree, 'my-file.cy.ts', 'angular');
|
||||
|
||||
expect(tree.read('my-file.cy.ts', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
it('should update angular react18 test file', () => {
|
||||
tree.write(
|
||||
'my-file.cy.ts',
|
||||
`
|
||||
import { mount } from 'cypress/react18';
|
||||
|
||||
describe('MyComponent', () => {
|
||||
it('should work', () => {
|
||||
mount(<MyComponent />);
|
||||
});
|
||||
|
||||
it('should work with config', () => {
|
||||
mount(<MyComponent title={"blah"}/>);
|
||||
});
|
||||
});
|
||||
`
|
||||
);
|
||||
updateCyFile(tree, 'my-file.cy.ts', 'react18');
|
||||
|
||||
expect(tree.read('my-file.cy.ts', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
it('should update angular react test file', () => {
|
||||
tree.write(
|
||||
'my-file.cy.ts',
|
||||
`
|
||||
import { mount } from 'cypress/react';
|
||||
|
||||
describe('MyComponent', () => {
|
||||
it('should work', () => {
|
||||
mount(<MyComponent />);
|
||||
});
|
||||
|
||||
it('should work with config', () => {
|
||||
mount(<MyComponent title={"blah"}/>,);
|
||||
});
|
||||
});
|
||||
`
|
||||
);
|
||||
updateCyFile(tree, 'my-file.cy.ts', 'react');
|
||||
|
||||
expect(tree.read('my-file.cy.ts', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
async function setup(tree: Tree) {
|
||||
await libraryGenerator(tree, { name: 'my-lib' });
|
||||
await libraryGenerator(tree, { name: 'another-lib' });
|
||||
await cypressComponentConfiguration(tree, {
|
||||
project: 'my-lib',
|
||||
skipFormat: false,
|
||||
});
|
||||
await cypressComponentConfiguration(tree, {
|
||||
project: 'another-lib',
|
||||
skipFormat: false,
|
||||
});
|
||||
const myLib = readProjectConfiguration(tree, 'my-lib');
|
||||
myLib.targets['build'] = {
|
||||
executor: '@nrwl/angular:webpack-browser',
|
||||
options: {},
|
||||
};
|
||||
myLib.targets['component-test'].executor = '@nrwl/cypress:cypress';
|
||||
myLib.targets['component-test'].options.devServerTarget = 'my-lib:build';
|
||||
updateProjectConfiguration(tree, 'my-lib', myLib);
|
||||
const anotherLib = readProjectConfiguration(tree, 'another-lib');
|
||||
anotherLib.targets['build'] = {
|
||||
executor: '@nrwl/webpack:webpack',
|
||||
options: {},
|
||||
};
|
||||
anotherLib.targets['component-test'].executor = '@nrwl/cypress:cypress';
|
||||
anotherLib.targets['component-test'].options.devServerTarget =
|
||||
'another-lib:build';
|
||||
updateProjectConfiguration(tree, 'another-lib', anotherLib);
|
||||
tree.write(
|
||||
'libs/my-lib/cypress/support/commands.ts',
|
||||
`
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
declare namespace Cypress {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
interface Chainable<Subject> {
|
||||
login(email: string, password: string): void;
|
||||
}
|
||||
}
|
||||
|
||||
// -- This is a parent command --
|
||||
Cypress.Commands.add('login', (email, password) => {
|
||||
console.log('Custom command example: Login', email, password);
|
||||
});`
|
||||
);
|
||||
tree.write(
|
||||
'libs/my-lib/src/lib/my-cmp-one.cy.js',
|
||||
`const { mount } =require('cypress/react');
|
||||
|
||||
describe('MyComponent', () => {
|
||||
it('should work', () => {
|
||||
mount(<MyComponent />);
|
||||
});
|
||||
|
||||
it('should work with config', () => {
|
||||
mount(<MyComponent title={"blah"}/>,);
|
||||
});
|
||||
});
|
||||
`
|
||||
);
|
||||
tree.write(
|
||||
'libs/my-lib/src/lib/my-cmp-two.cy.tsx',
|
||||
`import { mount } from 'cypress/react18';
|
||||
|
||||
describe('MyComponent', () => {
|
||||
it('should work', () => {
|
||||
mount(<MyComponent />);
|
||||
});
|
||||
|
||||
it('should work with config', () => {
|
||||
mount(<MyComponent title={"blah"}/>,);
|
||||
});
|
||||
});
|
||||
`
|
||||
);
|
||||
tree.write(
|
||||
'libs/my-lib/src/lib/my-cmp-three.cy.ts',
|
||||
`import { mount, MountConfig } from 'cypress/angular';
|
||||
|
||||
describe('MyComponent', () => {
|
||||
it('should work', () => {
|
||||
mount(MyComponent);
|
||||
});
|
||||
|
||||
it('should work with config', () => {
|
||||
mount(MyComponent, {...config, componentProperties: {foo: 'bar'}});
|
||||
});
|
||||
});
|
||||
`
|
||||
);
|
||||
|
||||
tree.write(
|
||||
'libs/another-lib/cypress/support/commands.ts',
|
||||
`
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
declare namespace Cypress {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
interface Chainable<Subject> {
|
||||
login(email: string, password: string): void;
|
||||
mount(c: any): void;
|
||||
}
|
||||
}
|
||||
|
||||
// -- This is a parent command --
|
||||
Cypress.Commands.add('login', (email, password) => {
|
||||
console.log('Custom command example: Login', email, password);
|
||||
});
|
||||
Cypress.Commands.add('mount', (any) => {
|
||||
console.log(mount);
|
||||
});
|
||||
`
|
||||
);
|
||||
|
||||
tree.write(
|
||||
'libs/another-lib/src/lib/my-cmp-one.cy.js',
|
||||
`const { mount } = require('cypress/react');
|
||||
|
||||
describe('MyComponent', () => {
|
||||
it('should work', () => {
|
||||
mount(<MyComponent />);
|
||||
});
|
||||
|
||||
it('should work with config', () => {
|
||||
mount(<MyComponent title={"blah"}/>,);
|
||||
});
|
||||
});
|
||||
`
|
||||
);
|
||||
tree.write(
|
||||
'libs/another-lib/src/lib/my-cmp-two.cy.tsx',
|
||||
`import { mount } from 'cypress/react18';
|
||||
|
||||
describe('MyComponent', () => {
|
||||
it('should work', () => {
|
||||
mount(<MyComponent />);
|
||||
});
|
||||
|
||||
it('should work with config', () => {
|
||||
mount(<MyComponent title={"blah"}/>,);
|
||||
});
|
||||
});
|
||||
`
|
||||
);
|
||||
tree.write(
|
||||
'libs/another-lib/src/lib/my-cmp-three.cy.ts',
|
||||
`import { mount, MountConfig } from 'cypress/angular';
|
||||
|
||||
describe('MyComponent', () => {
|
||||
it('should work', () => {
|
||||
mount(MyComponent);
|
||||
});
|
||||
|
||||
it('should work with config', () => {
|
||||
mount(MyComponent, {...config, componentProperties: {foo: 'bar'}});
|
||||
});
|
||||
});
|
||||
`
|
||||
);
|
||||
}
|
||||
@ -1,197 +0,0 @@
|
||||
import { CypressExecutorOptions } from '../../executors/cypress/cypress.impl';
|
||||
import { CY_FILE_MATCHER } from '../../utils/ct-helpers';
|
||||
import { installedCypressVersion } from '../../utils/cypress-version';
|
||||
import {
|
||||
createProjectGraphAsync,
|
||||
formatFiles,
|
||||
getProjects,
|
||||
joinPathFragments,
|
||||
parseTargetString,
|
||||
readJson,
|
||||
TargetConfiguration,
|
||||
Tree,
|
||||
visitNotIgnoredFiles,
|
||||
} from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { checkAndCleanWithSemver } from '@nx/devkit/src/utils/semver';
|
||||
import { tsquery } from '@phenomnomnominal/tsquery';
|
||||
import { gte } from 'semver';
|
||||
import * as ts from 'typescript';
|
||||
|
||||
export async function updateCyMountUsage(tree: Tree) {
|
||||
if (installedCypressVersion() < 10) {
|
||||
return;
|
||||
}
|
||||
|
||||
const projects = getProjects(tree);
|
||||
const graph = await createProjectGraphAsync();
|
||||
|
||||
forEachExecutorOptions<CypressExecutorOptions>(
|
||||
tree,
|
||||
'@nrwl/cypress:cypress',
|
||||
(options, projectName) => {
|
||||
if (options.testingType !== 'component' || !options.devServerTarget) {
|
||||
return;
|
||||
}
|
||||
|
||||
const parsed = parseTargetString(options.devServerTarget, graph);
|
||||
if (!parsed?.project || !parsed?.target) {
|
||||
return;
|
||||
}
|
||||
|
||||
const buildProjectConfig = projects.get(parsed.project);
|
||||
const framework = getFramework(
|
||||
tree,
|
||||
parsed.configuration
|
||||
? buildProjectConfig.targets[parsed.target].configurations[
|
||||
parsed.configuration
|
||||
]
|
||||
: buildProjectConfig.targets[parsed.target]
|
||||
);
|
||||
|
||||
const ctProjectConfig = projects.get(projectName);
|
||||
addMountCommand(tree, ctProjectConfig.root, framework);
|
||||
visitNotIgnoredFiles(tree, ctProjectConfig.sourceRoot, (filePath) => {
|
||||
if (CY_FILE_MATCHER.test(filePath)) {
|
||||
updateCyFile(tree, filePath, framework);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
export function addMountCommand(
|
||||
tree: Tree,
|
||||
projectRoot: string,
|
||||
framework: string
|
||||
) {
|
||||
const commandFilePath = joinPathFragments(
|
||||
projectRoot,
|
||||
'cypress',
|
||||
'support',
|
||||
'commands.ts'
|
||||
);
|
||||
if (!tree.exists(commandFilePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const commandFile = tree.read(commandFilePath, 'utf-8');
|
||||
const mountCommand = tsquery.query<ts.PropertyAccessExpression>(
|
||||
commandFile,
|
||||
'CallExpression:has(StringLiteral[value="mount"]) PropertyAccessExpression:has(Identifier[name="add"])'
|
||||
);
|
||||
if (mountCommand?.length > 0) {
|
||||
return;
|
||||
}
|
||||
const existingCommands = tsquery.query<
|
||||
ts.MethodSignature | ts.PropertySignature
|
||||
>(
|
||||
commandFile,
|
||||
'InterfaceDeclaration:has(Identifier[name="Chainable"]) > MethodSignature, InterfaceDeclaration:has(Identifier[name="Chainable"]) > PropertySignature'
|
||||
);
|
||||
const isGlobalDeclaration = tsquery.query<ts.ModuleDeclaration>(
|
||||
commandFile,
|
||||
'ModuleDeclaration > Identifier[name="global"]'
|
||||
);
|
||||
|
||||
const updatedInterface = tsquery.replace(
|
||||
commandFile,
|
||||
'ModuleDeclaration:has(Identifier[name="Cypress"])',
|
||||
(node: ts.ModuleDeclaration) => {
|
||||
const newModuleDelcaration = `declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
interface Chainable<Subject> {
|
||||
${existingCommands.map((c) => c.getText()).join('\n')}
|
||||
mount: typeof mount;
|
||||
}
|
||||
}
|
||||
}`;
|
||||
/*
|
||||
* this is to prevent the change being applied twice since
|
||||
* declare global { 1
|
||||
* interface Cypress { 2
|
||||
* }
|
||||
* }
|
||||
* matches twice.
|
||||
* i.e. if there is no global declaration, then add it
|
||||
* or if the node is the global declaration, then add it,
|
||||
* but not to the cypress module declaration inside the global declaration
|
||||
*/
|
||||
if (
|
||||
isGlobalDeclaration?.length === 0 ||
|
||||
node.name.getText() === 'global'
|
||||
) {
|
||||
return newModuleDelcaration;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const updatedCommandFile = `import { mount } from 'cypress/${framework}'\n${updatedInterface}\nCypress.Commands.add('mount', mount);`;
|
||||
tree.write(commandFilePath, updatedCommandFile);
|
||||
}
|
||||
|
||||
function getFramework(
|
||||
tree: Tree,
|
||||
target: TargetConfiguration
|
||||
): 'angular' | 'react' | 'react18' {
|
||||
if (
|
||||
target.executor === '@nrwl/angular:webpack-browser' ||
|
||||
target.executor === '@angular-devkit/build-angular:browser'
|
||||
) {
|
||||
return 'angular';
|
||||
}
|
||||
|
||||
const pkgJson = readJson(tree, 'package.json');
|
||||
const reactDomVersion = pkgJson?.dependencies?.['react-dom'];
|
||||
const hasReact18 =
|
||||
reactDomVersion &&
|
||||
gte(checkAndCleanWithSemver('react-dom', reactDomVersion), '18.0.0');
|
||||
|
||||
if (hasReact18) {
|
||||
return 'react18';
|
||||
}
|
||||
|
||||
return 'react';
|
||||
}
|
||||
|
||||
export function updateCyFile(
|
||||
tree: Tree,
|
||||
filePath: string,
|
||||
framework: 'angular' | 'react' | 'react18'
|
||||
) {
|
||||
if (!tree.exists(filePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const contents = tree.read(filePath, 'utf-8');
|
||||
const withCyMount = tsquery.replace(
|
||||
contents,
|
||||
':matches(CallExpression>Identifier[name="mount"])',
|
||||
(node: ts.CallExpression) => {
|
||||
return `cy.mount`;
|
||||
}
|
||||
);
|
||||
const withUpdatedImports = tsquery.replace(
|
||||
withCyMount,
|
||||
':matches(ImportDeclaration, VariableStatement):has(Identifier[name="mount"]):has(StringLiteral[value="cypress/react"], StringLiteral[value="cypress/angular"], StringLiteral[value="cypress/react18"])',
|
||||
(node: ts.ImportDeclaration) => {
|
||||
switch (framework) {
|
||||
case 'angular':
|
||||
return `import { MountConfig } from 'cypress/angular';`;
|
||||
case 'react18':
|
||||
case 'react':
|
||||
return ' '; // have to return non falsy string to remove the node
|
||||
default:
|
||||
return node.getText().replace('mount', '');
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
tree.write(filePath, withUpdatedImports);
|
||||
}
|
||||
|
||||
export default updateCyMountUsage;
|
||||
@ -1,198 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Cypress 11 Migration should migrate to v11 1`] = `
|
||||
"it('calls the prop', () => {
|
||||
cy.mount(<Comp onUnmount={cy.stub().as('onUnmount')} />);
|
||||
cy.contains('My component');
|
||||
});
|
||||
|
||||
describe('again', () => {
|
||||
it('calls the prop', () => {
|
||||
cy.mount(<Comp onUnmount={cy.stub().as('onUnmount')} />);
|
||||
cy.contains('My component');
|
||||
});
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Cypress 11 Migration should migrate to v11 2`] = `
|
||||
"/** TODO: mountHook is deprecate.
|
||||
* Use a wrapper component instead.
|
||||
* See post for details: https://www.cypress.io/blog/2022/11/04/upcoming-changes-to-component-testing/#reactmounthook-removed
|
||||
* */
|
||||
import { mountHook, getContainerEl } from 'cypress/react18';
|
||||
import ReactDom from 'react-dom';
|
||||
import { useCounter } from './useCounter';
|
||||
|
||||
it('increments the count', () => {
|
||||
mountHook(() => useCounter()).then((result) => {
|
||||
expect(result.current.count).to.equal(0);
|
||||
result.current.increment();
|
||||
expect(result.current.count).to.equal(1);
|
||||
result.current.increment();
|
||||
expect(result.current.count).to.equal(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('blah', () => {
|
||||
it('increments the count', () => {
|
||||
mountHook(() => useCounter()).then((result) => {
|
||||
expect(result.current.count).to.equal(0);
|
||||
result.current.increment();
|
||||
expect(result.current.count).to.equal(1);
|
||||
result.current.increment();
|
||||
expect(result.current.count).to.equal(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('calls the prop', () => {
|
||||
cy.mount(<Comp onUnmount={cy.stub().as('onUnmount')} />);
|
||||
cy.contains('My component');
|
||||
|
||||
cy.then(() => ReactDom.unmountComponentAtNode(getContainerEl()));
|
||||
|
||||
cy.contains('My component').should('not.exist');
|
||||
cy.get('@onUnmount').should('have.been.calledOnce');
|
||||
});
|
||||
|
||||
describe('again', () => {
|
||||
it('calls the prop', () => {
|
||||
cy.mount(<Comp onUnmount={cy.stub().as('onUnmount')} />);
|
||||
cy.contains('My component');
|
||||
|
||||
cy.then(() => ReactDom.unmountComponentAtNode(getContainerEl()));
|
||||
|
||||
cy.contains('My component').should('not.exist');
|
||||
cy.get('@onUnmount').should('have.been.calledOnce');
|
||||
});
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Cypress 11 Migration should migrate to v11 3`] = `
|
||||
"/** TODO: mountHook is deprecate.
|
||||
* Use a wrapper component instead.
|
||||
* See post for details: https://www.cypress.io/blog/2022/11/04/upcoming-changes-to-component-testing/#reactmounthook-removed
|
||||
* */
|
||||
import { mountHook, getContainerEl } from 'cypress/react';
|
||||
import ReactDom from 'react-dom';
|
||||
import { useCounter } from './useCounter';
|
||||
|
||||
it('increments the count', () => {
|
||||
mountHook(() => useCounter()).then((result) => {
|
||||
expect(result.current.count).to.equal(0);
|
||||
result.current.increment();
|
||||
expect(result.current.count).to.equal(1);
|
||||
result.current.increment();
|
||||
expect(result.current.count).to.equal(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('blah', () => {
|
||||
it('increments the count', () => {
|
||||
mountHook(() => useCounter()).then((result) => {
|
||||
expect(result.current.count).to.equal(0);
|
||||
result.current.increment();
|
||||
expect(result.current.count).to.equal(1);
|
||||
result.current.increment();
|
||||
expect(result.current.count).to.equal(2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('calls the prop', () => {
|
||||
cy.mount(<Comp onUnmount={cy.stub().as('onUnmount')} />);
|
||||
cy.contains('My component');
|
||||
|
||||
cy.then(() => ReactDom.unmountComponentAtNode(getContainerEl()));
|
||||
|
||||
cy.contains('My component').should('not.exist');
|
||||
cy.get('@onUnmount').should('have.been.calledOnce');
|
||||
});
|
||||
|
||||
describe('again', () => {
|
||||
it('calls the prop', () => {
|
||||
cy.mount(<Comp onUnmount={cy.stub().as('onUnmount')} />);
|
||||
cy.contains('My component');
|
||||
|
||||
cy.then(() => ReactDom.unmountComponentAtNode(getContainerEl()));
|
||||
|
||||
cy.contains('My component').should('not.exist');
|
||||
cy.get('@onUnmount').should('have.been.calledOnce');
|
||||
});
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Cypress 11 Migration should migrate to v11 4`] = `
|
||||
"import { TestBed } from '@angular/core/testing';
|
||||
|
||||
import { MyComponent } from './my.component';
|
||||
describe('MyComponent', () => {
|
||||
const config = {
|
||||
imports: [],
|
||||
declarations: [],
|
||||
providers: [{ provide: 'foo', useValue: 'bar' }],
|
||||
};
|
||||
it('direct usage', () => {
|
||||
TestBed.overrideComponent(MyComponent, {
|
||||
add: { providers: config.providers },
|
||||
});
|
||||
cy.mount(MyComponent, config);
|
||||
});
|
||||
it('spread usage', () => {
|
||||
TestBed.overrideComponent(MyComponent, {
|
||||
add: { providers: [{ provide: 'foo', useValue: 'bar' }] },
|
||||
});
|
||||
cy.mount(MyComponent, { ...config, providers: undefined });
|
||||
});
|
||||
it('inlined usage', () => {
|
||||
TestBed.overrideComponent(MyComponent, {
|
||||
add: { providers: [{ provide: 'foo', useValue: 'bar' }] },
|
||||
});
|
||||
cy.mount(MyComponent, {
|
||||
imports: [],
|
||||
declarations: [],
|
||||
providers: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Cypress 11 Migration should migrate to v11 5`] = `
|
||||
"import { MountConfig } from 'cypress/angular';
|
||||
import { MyComponent } from './my.component';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
describe('MyComponent', () => {
|
||||
const config: MountConfig = {
|
||||
imports: [],
|
||||
declarations: [],
|
||||
providers: [{ provide: 'foo', useValue: 'bar' }],
|
||||
};
|
||||
it('direct usage', () => {
|
||||
TestBed.overrideComponent(MyComponent, {
|
||||
add: { providers: config.providers },
|
||||
});
|
||||
cy.mount(MyComponent, config);
|
||||
});
|
||||
it('spread usage', () => {
|
||||
TestBed.overrideComponent(MyComponent, {
|
||||
add: { providers: [{ provide: 'foo', useValue: 'bar' }] },
|
||||
});
|
||||
cy.mount(MyComponent, { ...config, providers: undefined });
|
||||
});
|
||||
it('inlined usage', () => {
|
||||
TestBed.overrideComponent(MyComponent, {
|
||||
add: { providers: [{ provide: 'foo', useValue: 'bar' }] },
|
||||
});
|
||||
cy.mount(MyComponent, {
|
||||
imports: [],
|
||||
declarations: [],
|
||||
providers: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
"
|
||||
`;
|
||||
@ -1,344 +0,0 @@
|
||||
import 'nx/src/internal-testing-utils/mock-project-graph';
|
||||
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { libraryGenerator } from '@nx/js';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import updateToCypress11 from './cypress-11';
|
||||
import { installedCypressVersion } from '../../utils/cypress-version';
|
||||
|
||||
jest.mock('../../utils/cypress-version');
|
||||
import cypressComponentConfiguration from '../../generators/component-configuration/component-configuration';
|
||||
|
||||
describe('Cypress 11 Migration', () => {
|
||||
let tree: Tree;
|
||||
let mockInstalledCypressVersion: jest.Mock<
|
||||
ReturnType<typeof installedCypressVersion>
|
||||
> = installedCypressVersion as never;
|
||||
|
||||
// TODO(@leosvelperez): Turn this back to adding the plugin
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
});
|
||||
|
||||
it('should not update if cypress <v10 is used', async () => {
|
||||
// setup called the component setup. mock to v10 so it doesn't throw.
|
||||
mockInstalledCypressVersion.mockReturnValue(10);
|
||||
await setup(tree);
|
||||
mockInstalledCypressVersion.mockReturnValue(9);
|
||||
const beforeReact = tree.read(
|
||||
'libs/my-react-lib/src/lib/no-import.cy.ts',
|
||||
'utf-8'
|
||||
);
|
||||
|
||||
const beforeNg = tree.read(
|
||||
'libs/my-ng-lib/src/lib/no-import.component.cy.ts',
|
||||
'utf-8'
|
||||
);
|
||||
|
||||
await updateToCypress11(tree);
|
||||
const actualReact = tree.read(
|
||||
'libs/my-react-lib/src/lib/no-import.cy.ts',
|
||||
'utf-8'
|
||||
);
|
||||
|
||||
const actualNg = tree.read(
|
||||
'libs/my-ng-lib/src/lib/no-import.component.cy.ts',
|
||||
'utf-8'
|
||||
);
|
||||
|
||||
expect(actualReact).toEqual(beforeReact);
|
||||
expect(actualNg).toEqual(beforeNg);
|
||||
});
|
||||
|
||||
it('should migrate to v11', async () => {
|
||||
mockInstalledCypressVersion.mockReturnValue(10);
|
||||
await setup(tree);
|
||||
await updateToCypress11(tree);
|
||||
expect(
|
||||
tree.read('libs/my-react-lib/src/lib/no-import.cy.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
|
||||
expect(
|
||||
tree.read(
|
||||
'libs/my-react-lib/src/lib/with-import-18.component.cy.ts',
|
||||
'utf-8'
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read(
|
||||
'libs/my-react-lib/src/lib/with-import.component.cy.ts',
|
||||
'utf-8'
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('libs/my-ng-lib/src/lib/no-import.component.cy.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('libs/my-ng-lib/src/lib/with-import.component.cy.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should only update component projects', async () => {
|
||||
addProjectConfiguration(tree, 'my-e2e-app', {
|
||||
projectType: 'application',
|
||||
root: 'apps/my-e2e-app',
|
||||
sourceRoot: 'apps/my-e2e-app/src',
|
||||
targets: {
|
||||
e2e: {
|
||||
executor: '@nrwl/cypress:cypress',
|
||||
options: {
|
||||
cypressConfig: 'apps/my-e2e-app/cypress.config.ts',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const content = `import { MountConfig } from 'cypress/angular';
|
||||
import { MyComponent } from './my.component';
|
||||
describe('MyComponent', () => {
|
||||
const config: MountConfig = {
|
||||
imports: [],
|
||||
declarations: [],
|
||||
providers: [{ provide: 'foo', useValue: 'bar' }],
|
||||
};
|
||||
it('direct usage', () => {
|
||||
cy.mount(MyComponent, config);
|
||||
});
|
||||
});
|
||||
`;
|
||||
tree.write('apps/my-e2e-app/src/somthing.component.cy.ts', content);
|
||||
await updateToCypress11(tree);
|
||||
expect(
|
||||
tree.read('apps/my-e2e-app/src/somthing.component.cy.ts', 'utf-8')
|
||||
).toEqual(content);
|
||||
});
|
||||
});
|
||||
|
||||
async function setup(tree: Tree) {
|
||||
await libraryGenerator(tree, {
|
||||
name: 'my-react-lib',
|
||||
});
|
||||
await cypressComponentConfiguration(tree, {
|
||||
project: 'my-react-lib',
|
||||
skipFormat: true,
|
||||
});
|
||||
const projectConfig = readProjectConfiguration(tree, 'my-react-lib');
|
||||
projectConfig.targets['component-test'].executor = '@nrwl/cypress:cypress';
|
||||
updateProjectConfiguration(tree, 'my-react-lib', projectConfig);
|
||||
tree.write(
|
||||
'libs/my-react-lib/cypress/support/commands.ts',
|
||||
`/// <reference types="cypress" />
|
||||
import { mount } from 'cypress/react18'
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
interface Chainable<Subject> {
|
||||
login(email: string, password: string): void;
|
||||
mount: typeof mount;
|
||||
}
|
||||
}
|
||||
}
|
||||
Cypress.Commands.add('mount', mount)
|
||||
`
|
||||
);
|
||||
tree.write(
|
||||
'libs/my-react-lib/src/lib/no-import.cy.ts',
|
||||
`
|
||||
it('calls the prop', () => {
|
||||
cy.mount(<Comp onUnmount={cy.stub().as('onUnmount')} />)
|
||||
cy.contains('My component')
|
||||
})
|
||||
|
||||
describe('again', () => {
|
||||
it('calls the prop', () => {
|
||||
cy.mount(<Comp onUnmount={cy.stub().as('onUnmount')} />)
|
||||
cy.contains('My component')
|
||||
})
|
||||
})`
|
||||
);
|
||||
tree.write(
|
||||
'libs/my-react-lib/src/lib/with-import.component.cy.ts',
|
||||
`import { mountHook, unmount } from 'cypress/react'
|
||||
import { useCounter } from './useCounter'
|
||||
|
||||
it('increments the count', () => {
|
||||
mountHook(() => useCounter()).then((result) => {
|
||||
expect(result.current.count).to.equal(0)
|
||||
result.current.increment()
|
||||
expect(result.current.count).to.equal(1)
|
||||
result.current.increment()
|
||||
expect(result.current.count).to.equal(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('blah', () => {
|
||||
|
||||
it('increments the count', () => {
|
||||
mountHook(() => useCounter()).then((result) => {
|
||||
expect(result.current.count).to.equal(0)
|
||||
result.current.increment()
|
||||
expect(result.current.count).to.equal(1)
|
||||
result.current.increment()
|
||||
expect(result.current.count).to.equal(2)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
it('calls the prop', () => {
|
||||
cy.mount(<Comp onUnmount={cy.stub().as('onUnmount')} />)
|
||||
cy.contains('My component')
|
||||
|
||||
unmount()
|
||||
|
||||
cy.contains('My component').should('not.exist')
|
||||
cy.get('@onUnmount').should('have.been.calledOnce')
|
||||
})
|
||||
|
||||
describe('again', () => {
|
||||
it('calls the prop', () => {
|
||||
cy.mount(<Comp onUnmount={cy.stub().as('onUnmount')} />)
|
||||
cy.contains('My component')
|
||||
|
||||
unmount()
|
||||
|
||||
cy.contains('My component').should('not.exist')
|
||||
cy.get('@onUnmount').should('have.been.calledOnce')
|
||||
})
|
||||
})`
|
||||
);
|
||||
tree.write(
|
||||
'libs/my-react-lib/src/lib/with-import-18.component.cy.ts',
|
||||
`import { mountHook, unmount } from 'cypress/react18';
|
||||
import { useCounter } from './useCounter';
|
||||
|
||||
it('increments the count', () => {
|
||||
mountHook(() => useCounter()).then((result) => {
|
||||
expect(result.current.count).to.equal(0)
|
||||
result.current.increment()
|
||||
expect(result.current.count).to.equal(1)
|
||||
result.current.increment()
|
||||
expect(result.current.count).to.equal(2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('blah', () => {
|
||||
|
||||
it('increments the count', () => {
|
||||
mountHook(() => useCounter()).then((result) => {
|
||||
expect(result.current.count).to.equal(0)
|
||||
result.current.increment()
|
||||
expect(result.current.count).to.equal(1)
|
||||
result.current.increment()
|
||||
expect(result.current.count).to.equal(2)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
it('calls the prop', () => {
|
||||
cy.mount(<Comp onUnmount={cy.stub().as('onUnmount')} />)
|
||||
cy.contains('My component')
|
||||
|
||||
unmount()
|
||||
|
||||
cy.contains('My component').should('not.exist')
|
||||
cy.get('@onUnmount').should('have.been.calledOnce')
|
||||
})
|
||||
|
||||
describe('again', () => {
|
||||
it('calls the prop', () => {
|
||||
cy.mount(<Comp onUnmount={cy.stub().as('onUnmount')} />)
|
||||
cy.contains('My component')
|
||||
|
||||
unmount()
|
||||
|
||||
cy.contains('My component').should('not.exist')
|
||||
cy.get('@onUnmount').should('have.been.calledOnce')
|
||||
})
|
||||
})`
|
||||
);
|
||||
|
||||
await libraryGenerator(tree, {
|
||||
name: 'my-ng-lib',
|
||||
});
|
||||
|
||||
await cypressComponentConfiguration(tree, {
|
||||
project: 'my-ng-lib',
|
||||
skipFormat: true,
|
||||
});
|
||||
const projectConfig2 = readProjectConfiguration(tree, 'my-ng-lib');
|
||||
projectConfig2.targets['component-test'].executor = '@nrwl/cypress:cypress';
|
||||
updateProjectConfiguration(tree, 'my-ng-lib', projectConfig2);
|
||||
tree.write(
|
||||
'libs/my-ng-lib/cypress/support/commands.ts',
|
||||
`/// <reference types="cypress" />
|
||||
import { mount } from 'cypress/angular'
|
||||
|
||||
declare global {
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
namespace Cypress {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
interface Chainable<Subject> {
|
||||
login(email: string, password: string): void;
|
||||
mount: typeof mount;
|
||||
}
|
||||
}
|
||||
}
|
||||
Cypress.Commands.add('mount', mount)
|
||||
`
|
||||
);
|
||||
tree.write(
|
||||
'libs/my-ng-lib/src/lib/with-import.component.cy.ts',
|
||||
`import { MountConfig } from 'cypress/angular';
|
||||
import { MyComponent } from './my.component';
|
||||
import {TestBed} from '@angular/core/testing';
|
||||
describe('MyComponent', () => {
|
||||
const config: MountConfig = {
|
||||
imports: [],
|
||||
declarations: [],
|
||||
providers: [{provide: 'foo', useValue: 'bar'}]
|
||||
};
|
||||
it('direct usage', () => {
|
||||
cy.mount(MyComponent, config);
|
||||
});
|
||||
it('spread usage', () => {
|
||||
cy.mount(MyComponent, {...config, providers: [{provide: 'foo', useValue: 'bar'}] });
|
||||
});
|
||||
it('inlined usage', () => {
|
||||
cy.mount(MyComponent, {imports: [], declarations: [], providers: [{provide: 'foo', useValue: 'bar'}]});
|
||||
});
|
||||
});
|
||||
`
|
||||
);
|
||||
tree.write(
|
||||
'libs/my-ng-lib/src/lib/no-import.component.cy.ts',
|
||||
`
|
||||
import { MyComponent } from './my.component';
|
||||
describe('MyComponent', () => {
|
||||
const config = {
|
||||
imports: [],
|
||||
declarations: [],
|
||||
providers: [{provide: 'foo', useValue: 'bar'}]
|
||||
};
|
||||
it('direct usage', () => {
|
||||
cy.mount(MyComponent, config);
|
||||
});
|
||||
it('spread usage', () => {
|
||||
cy.mount(MyComponent, {...config, providers: [{provide: 'foo', useValue: 'bar'}] });
|
||||
});
|
||||
it('inlined usage', () => {
|
||||
cy.mount(MyComponent, {imports: [], declarations: [], providers: [{provide: 'foo', useValue: 'bar'}]});
|
||||
});
|
||||
});
|
||||
`
|
||||
);
|
||||
}
|
||||
@ -1,180 +0,0 @@
|
||||
import { CY_FILE_MATCHER } from '../../utils/ct-helpers';
|
||||
import {
|
||||
addDependenciesToPackageJson,
|
||||
formatFiles,
|
||||
getProjects,
|
||||
joinPathFragments,
|
||||
Tree,
|
||||
visitNotIgnoredFiles,
|
||||
} from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { tsquery } from '@phenomnomnominal/tsquery';
|
||||
import { extname } from 'path';
|
||||
import * as ts from 'typescript';
|
||||
import { CypressExecutorOptions } from '../../executors/cypress/cypress.impl';
|
||||
import { installedCypressVersion } from '../../utils/cypress-version';
|
||||
import { cypressVersion } from '../../utils/versions';
|
||||
|
||||
export async function updateToCypress11(tree: Tree) {
|
||||
const installedVersion = installedCypressVersion();
|
||||
if (installedVersion < 10) {
|
||||
return;
|
||||
}
|
||||
|
||||
const projects = getProjects(tree);
|
||||
forEachExecutorOptions<CypressExecutorOptions>(
|
||||
tree,
|
||||
'@nrwl/cypress:cypress',
|
||||
(options, projectName, targetName, configurationName) => {
|
||||
if (
|
||||
options.testingType !== 'component' ||
|
||||
!(options.cypressConfig && tree.exists(options.cypressConfig))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const projectConfig = projects.get(projectName);
|
||||
const commandsFile = joinPathFragments(
|
||||
projectConfig.root,
|
||||
'cypress',
|
||||
'support',
|
||||
'commands.ts'
|
||||
);
|
||||
const framework = getFramework(
|
||||
tree.exists(commandsFile)
|
||||
? tree.read(commandsFile, 'utf-8')
|
||||
: tree.read(options.cypressConfig, 'utf-8')
|
||||
);
|
||||
|
||||
visitNotIgnoredFiles(tree, projectConfig.sourceRoot, (filePath) => {
|
||||
if (!CY_FILE_MATCHER.test(filePath)) {
|
||||
return;
|
||||
}
|
||||
const frameworkFromFile = getFramework(tree.read(filePath, 'utf-8'));
|
||||
|
||||
if (framework === 'react' || frameworkFromFile === 'react') {
|
||||
updateUnmountUsage(tree, filePath);
|
||||
updateMountHookUsage(tree, filePath);
|
||||
}
|
||||
if (framework === 'angular' || frameworkFromFile === 'angular') {
|
||||
updateProviderUsage(tree, filePath);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
const installTask = addDependenciesToPackageJson(
|
||||
tree,
|
||||
{},
|
||||
{ cypress: cypressVersion }
|
||||
);
|
||||
|
||||
await formatFiles(tree);
|
||||
return () => {
|
||||
installTask();
|
||||
};
|
||||
}
|
||||
|
||||
export function updateMountHookUsage(tree: Tree, filePath: string) {
|
||||
const originalContents = tree.read(filePath, 'utf-8');
|
||||
const commentedMountHook = tsquery.replace(
|
||||
originalContents,
|
||||
':matches(ImportDeclaration, VariableStatement):has(Identifier[name="mountHook"]):has(StringLiteral[value="cypress/react"], StringLiteral[value="cypress/react18"])',
|
||||
(node) => {
|
||||
return `/** TODO: mountHook is deprecate.
|
||||
* Use a wrapper component instead.
|
||||
* See post for details: https://www.cypress.io/blog/2022/11/04/upcoming-changes-to-component-testing/#reactmounthook-removed
|
||||
* */\n${node.getText()}`;
|
||||
}
|
||||
);
|
||||
|
||||
tree.write(filePath, commentedMountHook);
|
||||
}
|
||||
|
||||
export function updateUnmountUsage(tree: Tree, filePath: string) {
|
||||
const reactDomImport = extname(filePath).includes('ts')
|
||||
? `import ReactDom from 'react-dom'`
|
||||
: `const ReactDom = require('react-dom')`;
|
||||
|
||||
const originalContents = tree.read(filePath, 'utf-8');
|
||||
|
||||
const updatedImports = tsquery.replace(
|
||||
originalContents,
|
||||
':matches(ImportDeclaration, VariableStatement):has(Identifier[name="unmount"]):has(StringLiteral[value="cypress/react"], StringLiteral[value="cypress/react18"])',
|
||||
(node) => {
|
||||
return `${node.getText().replace('unmount', 'getContainerEl')}
|
||||
${reactDomImport}`;
|
||||
}
|
||||
);
|
||||
|
||||
const updatedUnmountApi = tsquery.replace(
|
||||
updatedImports,
|
||||
'ExpressionStatement > CallExpression:has(Identifier[name="unmount"])',
|
||||
(node: ts.ExpressionStatement) => {
|
||||
if (node.expression.getText() === 'unmount') {
|
||||
return `cy.then(() => ReactDom.unmountComponentAtNode(getContainerEl()))`;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
tree.write(filePath, updatedUnmountApi);
|
||||
}
|
||||
|
||||
export function updateProviderUsage(tree: Tree, filePath: string) {
|
||||
const originalContents = tree.read(filePath, 'utf-8');
|
||||
const isTestBedImported =
|
||||
tsquery.query(
|
||||
originalContents,
|
||||
':matches(ImportDeclaration, VariableStatement):has(Identifier[name="TestBed"]):has(StringLiteral[value="@angular/core/testing"])'
|
||||
)?.length > 0;
|
||||
|
||||
let updatedProviders = tsquery.replace(
|
||||
originalContents,
|
||||
'CallExpression:has(PropertyAccessExpression:has(Identifier[name="mount"]))',
|
||||
(node: ts.CallExpression) => {
|
||||
const expressionName = node.expression.getText();
|
||||
if (expressionName === 'cy.mount' && node?.arguments?.length > 1) {
|
||||
const component = node.arguments[0].getText();
|
||||
|
||||
if (ts.isObjectLiteralExpression(node.arguments[1])) {
|
||||
const providers = node.arguments[1]?.properties
|
||||
?.find((p) => p.name?.getText() === 'providers')
|
||||
?.getText();
|
||||
const noProviders = tsquery.replace(
|
||||
node.getText(),
|
||||
'PropertyAssignment:has(Identifier[name="providers"])',
|
||||
(n) => {
|
||||
// set it to undefined so we don't run into a hanging comma causing invalid syntax
|
||||
return 'providers: undefined';
|
||||
}
|
||||
);
|
||||
return `TestBed.overrideComponent(${component}, { add: { ${providers} }});\n${noProviders}`;
|
||||
} else {
|
||||
return `TestBed.overrideComponent(${component}, {add: { providers: ${node.arguments[1].getText()}.providers}});\n${node.getText()}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
tree.write(
|
||||
filePath,
|
||||
`${
|
||||
isTestBedImported
|
||||
? ''
|
||||
: "import {TestBed} from '@angular/core/testing';\n"
|
||||
}${updatedProviders}`
|
||||
);
|
||||
}
|
||||
|
||||
function getFramework(contents: string): 'react' | 'angular' | null {
|
||||
if (contents.includes('cypress/react') || contents.includes('@nrwl/react')) {
|
||||
return 'react';
|
||||
}
|
||||
if (
|
||||
contents.includes('cypress/angular') ||
|
||||
contents.includes('@nrwl/angular')
|
||||
) {
|
||||
return 'angular';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export default updateToCypress11;
|
||||
@ -1,87 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Cypress 12 Migration should migrate to cy 12 1`] = `
|
||||
"describe('something', () => {
|
||||
it('should do the thing', () => {
|
||||
// TODO(@nrwl/cypress): this command has been removed, use cy.session instead. https://docs.cypress.io/guides/references/migration-guide#Command-Cypress-API-Changes
|
||||
Cypress.Cookies.defaults()
|
||||
// TODO(@nrwl/cypress): this command has been removed, use cy.session instead. https://docs.cypress.io/guides/references/migration-guide#Command-Cypress-API-Changes
|
||||
Cypress.Cookies.preserveOnce('seesion_id', 'remember-token');
|
||||
Cypress.blah.abc()
|
||||
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||
Cypress.Server.defaults({
|
||||
delay: 500,
|
||||
method: 'GET',
|
||||
})
|
||||
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||
cy.server()
|
||||
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||
cy.route(/api/, () => {
|
||||
return {
|
||||
'test': 'We’ll',
|
||||
}
|
||||
}).as('getApi')
|
||||
|
||||
cy.visit('/index.html')
|
||||
cy.window().then((win) => {
|
||||
const xhr = new win.XMLHttpRequest
|
||||
xhr.open('GET', '/api/v1/foo/bar?a=42')
|
||||
xhr.send()
|
||||
})
|
||||
|
||||
cy.wait('@getApi')
|
||||
.its('url').should('include', 'api/v1')
|
||||
/**
|
||||
* TODO(@nrwl/cypress): Nesting Cypress commands in a should assertion now throws.
|
||||
* You should use .then() to chain commands instead.
|
||||
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
|
||||
**/
|
||||
cy.should(($s) => {
|
||||
cy.get('@table').find('tr').should('have.length', 3)
|
||||
})
|
||||
})
|
||||
})"
|
||||
`;
|
||||
|
||||
exports[`Cypress 12 Migration should migrate to cy 12 2`] = `
|
||||
"describe('something', () => {
|
||||
it('should do the thing', () => {
|
||||
// TODO(@nrwl/cypress): this command has been removed, use cy.session instead. https://docs.cypress.io/guides/references/migration-guide#Command-Cypress-API-Changes
|
||||
Cypress.Cookies.defaults()
|
||||
// TODO(@nrwl/cypress): this command has been removed, use cy.session instead. https://docs.cypress.io/guides/references/migration-guide#Command-Cypress-API-Changes
|
||||
Cypress.Cookies.preserveOnce('seesion_id', 'remember-token');
|
||||
Cypress.blah.abc()
|
||||
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||
Cypress.Server.defaults({
|
||||
delay: 500,
|
||||
method: 'GET',
|
||||
})
|
||||
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||
cy.server()
|
||||
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||
cy.route(/api/, () => {
|
||||
return {
|
||||
'test': 'We’ll',
|
||||
}
|
||||
}).as('getApi')
|
||||
|
||||
cy.visit('/index.html')
|
||||
cy.window().then((win) => {
|
||||
const xhr = new win.XMLHttpRequest
|
||||
xhr.open('GET', '/api/v1/foo/bar?a=42')
|
||||
xhr.send()
|
||||
})
|
||||
|
||||
cy.wait('@getApi')
|
||||
.its('url').should('include', 'api/v1')
|
||||
/**
|
||||
* TODO(@nrwl/cypress): Nesting Cypress commands in a should assertion now throws.
|
||||
* You should use .then() to chain commands instead.
|
||||
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
|
||||
**/
|
||||
cy.should(($s) => {
|
||||
cy.get('@table').find('tr').should('have.length', 3)
|
||||
})
|
||||
})
|
||||
})"
|
||||
`;
|
||||
@ -1,717 +0,0 @@
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
stripIndents,
|
||||
Tree,
|
||||
readJson,
|
||||
} from '@nx/devkit';
|
||||
import {
|
||||
shouldNotOverrideCommands,
|
||||
shouldNotUseCyInShouldCB,
|
||||
shouldUseCyIntercept,
|
||||
shouldUseCySession,
|
||||
turnOffTestIsolation,
|
||||
updateToCypress12,
|
||||
} from './update-to-cypress-12';
|
||||
import { installedCypressVersion } from '../../utils/cypress-version';
|
||||
jest.mock('../../utils/cypress-version');
|
||||
|
||||
describe('Cypress 12 Migration', () => {
|
||||
let tree: Tree;
|
||||
let mockInstalledCypressVersion: jest.Mock<
|
||||
ReturnType<typeof installedCypressVersion>
|
||||
> = installedCypressVersion as never;
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace();
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it('should migrate to cy 12', () => {
|
||||
mockInstalledCypressVersion.mockReturnValue(11);
|
||||
addCypressProject(tree, 'my-app-e2e');
|
||||
addCypressProject(tree, 'my-other-app-e2e');
|
||||
updateToCypress12(tree);
|
||||
assertMigration(tree, 'my-app-e2e');
|
||||
assertMigration(tree, 'my-other-app-e2e');
|
||||
const pkgJson = readJson(tree, 'package.json');
|
||||
expect(pkgJson.devDependencies['cypress']).toEqual('^12.2.0');
|
||||
});
|
||||
|
||||
it('should not migrate if cypress version is < 11', () => {
|
||||
mockInstalledCypressVersion.mockReturnValue(10);
|
||||
addCypressProject(tree, 'my-app-e2e');
|
||||
updateToCypress12(tree);
|
||||
expect(tree.read('apps/my-app-e2e/cypress.config.ts', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"import { defineConfig } from 'cypress';
|
||||
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||
|
||||
export default defineConfig({
|
||||
e2e: nxE2EPreset(__filename)
|
||||
})"
|
||||
`);
|
||||
});
|
||||
|
||||
describe('nest cypress commands in should callback', () => {
|
||||
beforeEach(() => {
|
||||
tree.write(
|
||||
'should-callback.ts',
|
||||
`describe('something', () => {
|
||||
it('should do the thing', () => {
|
||||
Cypress.Cookies.defaults()
|
||||
cy.server()
|
||||
|
||||
cy.wait('@getApi')
|
||||
.its('url').should('include', 'api/v1')
|
||||
cy.should((b) => {
|
||||
const a = 123;
|
||||
// I'm not doing nested cy stuff
|
||||
});
|
||||
cy.should(($s) => {
|
||||
cy.task("");
|
||||
})
|
||||
cy.should(function($el) {
|
||||
cy.task("");
|
||||
})
|
||||
})
|
||||
})
|
||||
`
|
||||
);
|
||||
});
|
||||
it('should comment', () => {
|
||||
shouldNotUseCyInShouldCB(tree, 'should-callback.ts');
|
||||
expect(tree.read('should-callback.ts', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"describe('something', () => {
|
||||
it('should do the thing', () => {
|
||||
Cypress.Cookies.defaults()
|
||||
cy.server()
|
||||
|
||||
cy.wait('@getApi')
|
||||
.its('url').should('include', 'api/v1')
|
||||
cy.should((b) => {
|
||||
const a = 123;
|
||||
// I'm not doing nested cy stuff
|
||||
});
|
||||
/**
|
||||
* TODO(@nrwl/cypress): Nesting Cypress commands in a should assertion now throws.
|
||||
* You should use .then() to chain commands instead.
|
||||
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
|
||||
**/
|
||||
cy.should(($s) => {
|
||||
cy.task("");
|
||||
})
|
||||
/**
|
||||
* TODO(@nrwl/cypress): Nesting Cypress commands in a should assertion now throws.
|
||||
* You should use .then() to chain commands instead.
|
||||
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
|
||||
**/
|
||||
cy.should(function($el) {
|
||||
cy.task("");
|
||||
})
|
||||
})
|
||||
})
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should be idempotent', () => {
|
||||
const expected = `describe('something', () => {
|
||||
it('should do the thing', () => {
|
||||
Cypress.Cookies.defaults()
|
||||
cy.server()
|
||||
|
||||
cy.wait('@getApi')
|
||||
.its('url').should('include', 'api/v1')
|
||||
cy.should((b) => {
|
||||
const a = 123;
|
||||
// I'm not doing nested cy stuff
|
||||
});
|
||||
/**
|
||||
* TODO(@nrwl/cypress): Nesting Cypress commands in a should assertion now throws.
|
||||
* You should use .then() to chain commands instead.
|
||||
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
|
||||
**/
|
||||
cy.should(($s) => {
|
||||
cy.task("");
|
||||
})
|
||||
/**
|
||||
* TODO(@nrwl/cypress): Nesting Cypress commands in a should assertion now throws.
|
||||
* You should use .then() to chain commands instead.
|
||||
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
|
||||
**/
|
||||
cy.should(function($el) {
|
||||
cy.task("");
|
||||
})
|
||||
})
|
||||
})
|
||||
`;
|
||||
shouldNotUseCyInShouldCB(tree, 'should-callback.ts');
|
||||
expect(tree.read('should-callback.ts', 'utf-8')).toEqual(expected);
|
||||
shouldNotUseCyInShouldCB(tree, 'should-callback.ts');
|
||||
expect(tree.read('should-callback.ts', 'utf-8')).toEqual(expected);
|
||||
});
|
||||
});
|
||||
describe('banned Cypres.Commands.overwrite', () => {
|
||||
beforeEach(() => {
|
||||
tree.write(
|
||||
'commands.ts',
|
||||
`declare namespace Cypress {
|
||||
interface Chainable<Subject> {
|
||||
login(email: string, password: string): void;
|
||||
}
|
||||
}
|
||||
//
|
||||
// -- This is a parent command --
|
||||
Cypress.Commands.add('login', (email, password) => {
|
||||
console.log('Custom command example: Login', email, password);
|
||||
});
|
||||
Cypress.Commands.overwrite('find', () => {});
|
||||
`
|
||||
);
|
||||
});
|
||||
it('should comment', () => {
|
||||
shouldNotOverrideCommands(tree, 'commands.ts');
|
||||
expect(tree.read('commands.ts', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"declare namespace Cypress {
|
||||
interface Chainable<Subject> {
|
||||
login(email: string, password: string): void;
|
||||
}
|
||||
}
|
||||
//
|
||||
// -- This is a parent command --
|
||||
Cypress.Commands.add('login', (email, password) => {
|
||||
console.log('Custom command example: Login', email, password);
|
||||
});
|
||||
/**
|
||||
* TODO(@nrwl/cypress): This command can no longer be overridden
|
||||
* Consider using a different name like 'custom_find'
|
||||
* More info: https://docs.cypress.io/guides/references/migration-guide#Cypress-Commands-overwrite
|
||||
**/
|
||||
Cypress.Commands.overwrite('find', () => {});
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should be idempotent', () => {
|
||||
const expected = `declare namespace Cypress {
|
||||
interface Chainable<Subject> {
|
||||
login(email: string, password: string): void;
|
||||
}
|
||||
}
|
||||
//
|
||||
// -- This is a parent command --
|
||||
Cypress.Commands.add('login', (email, password) => {
|
||||
console.log('Custom command example: Login', email, password);
|
||||
});
|
||||
/**
|
||||
* TODO(@nrwl/cypress): This command can no longer be overridden
|
||||
* Consider using a different name like 'custom_find'
|
||||
* More info: https://docs.cypress.io/guides/references/migration-guide#Cypress-Commands-overwrite
|
||||
**/
|
||||
Cypress.Commands.overwrite('find', () => {});
|
||||
`;
|
||||
|
||||
shouldNotOverrideCommands(tree, 'commands.ts');
|
||||
expect(tree.read('commands.ts', 'utf-8')).toEqual(expected);
|
||||
shouldNotOverrideCommands(tree, 'commands.ts');
|
||||
expect(tree.read('commands.ts', 'utf-8')).toEqual(expected);
|
||||
});
|
||||
});
|
||||
describe('api removal', () => {
|
||||
it('should be idempotent', () => {
|
||||
tree.write(
|
||||
'my-cool-test.cy.ts',
|
||||
`
|
||||
describe('something', () => {
|
||||
it('should do the thing', () => {
|
||||
Cypress.Cookies.defaults()
|
||||
Cypress.Cookies.preserveOnce('seesion_id', 'remember-token');
|
||||
Cypress.blah.abc()
|
||||
Cypress.Server.defaults({
|
||||
delay: 500,
|
||||
method: 'GET',
|
||||
})
|
||||
cy.server()
|
||||
cy.route(/api/, () => {
|
||||
return {
|
||||
'test': 'We’ll',
|
||||
}
|
||||
}).as('getApi')
|
||||
|
||||
cy.visit('/index.html')
|
||||
cy.window().then((win) => {
|
||||
const xhr = new win.XMLHttpRequest
|
||||
xhr.open('GET', '/api/v1/foo/bar?a=42')
|
||||
xhr.send()
|
||||
})
|
||||
|
||||
cy.wait('@getApi')
|
||||
.its('url').should('include', 'api/v1')
|
||||
})
|
||||
})
|
||||
`
|
||||
);
|
||||
const expected = stripIndents`describe('something', () => {
|
||||
it('should do the thing', () => {
|
||||
// TODO(@nrwl/cypress): this command has been removed, use cy.session instead. https://docs.cypress.io/guides/references/migration-guide#Command-Cypress-API-Changes
|
||||
Cypress.Cookies.defaults()
|
||||
// TODO(@nrwl/cypress): this command has been removed, use cy.session instead. https://docs.cypress.io/guides/references/migration-guide#Command-Cypress-API-Changes
|
||||
Cypress.Cookies.preserveOnce('seesion_id', 'remember-token');
|
||||
Cypress.blah.abc()
|
||||
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||
Cypress.Server.defaults({
|
||||
delay: 500,
|
||||
method: 'GET',
|
||||
})
|
||||
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||
cy.server()
|
||||
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||
cy.route(/api/, () => {
|
||||
return {
|
||||
'test': 'We’ll',
|
||||
}
|
||||
}).as('getApi')
|
||||
|
||||
cy.visit('/index.html')
|
||||
cy.window().then((win) => {
|
||||
const xhr = new win.XMLHttpRequest
|
||||
xhr.open('GET', '/api/v1/foo/bar?a=42')
|
||||
xhr.send()
|
||||
})
|
||||
|
||||
cy.wait('@getApi')
|
||||
.its('url').should('include', 'api/v1')
|
||||
})
|
||||
})`;
|
||||
shouldUseCyIntercept(tree, 'my-cool-test.cy.ts');
|
||||
shouldUseCySession(tree, 'my-cool-test.cy.ts');
|
||||
expect(stripIndents`${tree.read('my-cool-test.cy.ts', 'utf-8')}`).toEqual(
|
||||
expected
|
||||
);
|
||||
|
||||
shouldUseCyIntercept(tree, 'my-cool-test.cy.ts');
|
||||
shouldUseCySession(tree, 'my-cool-test.cy.ts');
|
||||
expect(stripIndents`${tree.read('my-cool-test.cy.ts', 'utf-8')}`).toEqual(
|
||||
expected
|
||||
);
|
||||
});
|
||||
it('comment on cy.route,cy.server, & Cypress.Server.defaults usage', () => {
|
||||
tree.write(
|
||||
'my-cool-test.cy.ts',
|
||||
`
|
||||
describe('something', () => {
|
||||
it('should do the thing', () => {
|
||||
Cypress.Cookies.defaults()
|
||||
Cypress.Cookies.preserveOnce('seesion_id', 'remember-token');
|
||||
Cypress.blah.abc()
|
||||
Cypress.Server.defaults({
|
||||
delay: 500,
|
||||
method: 'GET',
|
||||
})
|
||||
cy.server()
|
||||
cy.route(/api/, () => {
|
||||
return {
|
||||
'test': 'We’ll',
|
||||
}
|
||||
}).as('getApi')
|
||||
|
||||
cy.visit('/index.html')
|
||||
cy.window().then((win) => {
|
||||
const xhr = new win.XMLHttpRequest
|
||||
xhr.open('GET', '/api/v1/foo/bar?a=42')
|
||||
xhr.send()
|
||||
})
|
||||
|
||||
cy.wait('@getApi')
|
||||
.its('url').should('include', 'api/v1')
|
||||
})
|
||||
})
|
||||
`
|
||||
);
|
||||
shouldUseCyIntercept(tree, 'my-cool-test.cy.ts');
|
||||
expect(tree.read('my-cool-test.cy.ts', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"
|
||||
describe('something', () => {
|
||||
it('should do the thing', () => {
|
||||
Cypress.Cookies.defaults()
|
||||
Cypress.Cookies.preserveOnce('seesion_id', 'remember-token');
|
||||
Cypress.blah.abc()
|
||||
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||
Cypress.Server.defaults({
|
||||
delay: 500,
|
||||
method: 'GET',
|
||||
})
|
||||
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||
cy.server()
|
||||
// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||
cy.route(/api/, () => {
|
||||
return {
|
||||
'test': 'We’ll',
|
||||
}
|
||||
}).as('getApi')
|
||||
|
||||
cy.visit('/index.html')
|
||||
cy.window().then((win) => {
|
||||
const xhr = new win.XMLHttpRequest
|
||||
xhr.open('GET', '/api/v1/foo/bar?a=42')
|
||||
xhr.send()
|
||||
})
|
||||
|
||||
cy.wait('@getApi')
|
||||
.its('url').should('include', 'api/v1')
|
||||
})
|
||||
})
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('comment on Cypress.Cookies.defaults & Cypress.Cookies.preserveOnce', () => {
|
||||
tree.write(
|
||||
'my-cool-test.cy.ts',
|
||||
`
|
||||
describe('something', () => {
|
||||
it('should do the thing', () => {
|
||||
Cypress.Cookies.defaults()
|
||||
Cypress.Cookies.preserveOnce('seesion_id', 'remember-token');
|
||||
Cypress.blah.abc()
|
||||
Cypress.Server.defaults({
|
||||
delay: 500,
|
||||
method: 'GET',
|
||||
})
|
||||
cy.server()
|
||||
|
||||
cy.wait('@getApi')
|
||||
.its('url').should('include', 'api/v1')
|
||||
})
|
||||
})
|
||||
`
|
||||
);
|
||||
shouldUseCySession(tree, 'my-cool-test.cy.ts');
|
||||
expect(tree.read('my-cool-test.cy.ts', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"
|
||||
describe('something', () => {
|
||||
it('should do the thing', () => {
|
||||
// TODO(@nrwl/cypress): this command has been removed, use cy.session instead. https://docs.cypress.io/guides/references/migration-guide#Command-Cypress-API-Changes
|
||||
Cypress.Cookies.defaults()
|
||||
// TODO(@nrwl/cypress): this command has been removed, use cy.session instead. https://docs.cypress.io/guides/references/migration-guide#Command-Cypress-API-Changes
|
||||
Cypress.Cookies.preserveOnce('seesion_id', 'remember-token');
|
||||
Cypress.blah.abc()
|
||||
Cypress.Server.defaults({
|
||||
delay: 500,
|
||||
method: 'GET',
|
||||
})
|
||||
cy.server()
|
||||
|
||||
cy.wait('@getApi')
|
||||
.its('url').should('include', 'api/v1')
|
||||
})
|
||||
})
|
||||
"
|
||||
`);
|
||||
});
|
||||
});
|
||||
describe('testIsolation', () => {
|
||||
it('should be idempotent', () => {
|
||||
const content = `
|
||||
import { defineConfig } from 'cypress';
|
||||
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||
|
||||
export default defineConfig({
|
||||
e2e: {
|
||||
...nxE2EPreset(__filename),
|
||||
testIsolation: true,
|
||||
})
|
||||
`;
|
||||
tree.write('my-cypress.config.ts', content);
|
||||
turnOffTestIsolation(tree, 'my-cypress.config.ts');
|
||||
|
||||
expect(tree.read('my-cypress.config.ts', 'utf-8')).toEqual(content);
|
||||
});
|
||||
it('should add testIsolation: false to the default e2e config', () => {
|
||||
tree.write(
|
||||
'my-cypress.config.ts',
|
||||
`
|
||||
import { defineConfig } from 'cypress';
|
||||
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||
|
||||
export default defineConfig({
|
||||
e2e: nxE2EPreset(__filename),
|
||||
})
|
||||
`
|
||||
);
|
||||
turnOffTestIsolation(tree, 'my-cypress.config.ts');
|
||||
expect(tree.read('my-cypress.config.ts', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"
|
||||
import { defineConfig } from 'cypress';
|
||||
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||
|
||||
export default defineConfig({
|
||||
e2e: {
|
||||
...nxE2EPreset(__filename),
|
||||
/**
|
||||
* TODO(@nrwl/cypress): In Cypress v12,the testIsolation option is turned on by default.
|
||||
* This can cause tests to start breaking where not indended.
|
||||
* You should consider enabling this once you verify tests do not depend on each other
|
||||
* More Info: https://docs.cypress.io/guides/references/migration-guide#Test-Isolation
|
||||
**/
|
||||
testIsolation: false,
|
||||
},
|
||||
})
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should add testIsolation: false to inline object e2e config', () => {
|
||||
tree.write(
|
||||
'my-cypress.config.ts',
|
||||
`
|
||||
import { defineConfig } from 'cypress';
|
||||
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||
|
||||
export default defineConfig({
|
||||
e2e: {
|
||||
...nxE2EPreset(__filename),
|
||||
video: false
|
||||
}
|
||||
})
|
||||
`
|
||||
);
|
||||
turnOffTestIsolation(tree, 'my-cypress.config.ts');
|
||||
expect(tree.read('my-cypress.config.ts', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"
|
||||
import { defineConfig } from 'cypress';
|
||||
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||
|
||||
export default defineConfig({
|
||||
e2e: {
|
||||
...nxE2EPreset(__filename),
|
||||
video: false,
|
||||
/**
|
||||
* TODO(@nrwl/cypress): In Cypress v12,the testIsolation option is turned on by default.
|
||||
* This can cause tests to start breaking where not indended.
|
||||
* You should consider enabling this once you verify tests do not depend on each other
|
||||
* More Info: https://docs.cypress.io/guides/references/migration-guide#Test-Isolation
|
||||
**/
|
||||
testIsolation: false,
|
||||
}
|
||||
})
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should add testIsolation: false for a variable e2e config', () => {
|
||||
tree.write(
|
||||
'my-cypress.config.ts',
|
||||
`
|
||||
import { defineConfig } from 'cypress';
|
||||
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||
const myConfig = {
|
||||
...nxE2EPreset(__filename),
|
||||
video: false
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
e2e: myConfig,
|
||||
})
|
||||
`
|
||||
);
|
||||
turnOffTestIsolation(tree, 'my-cypress.config.ts');
|
||||
expect(tree.read('my-cypress.config.ts', 'utf-8')).toMatchInlineSnapshot(`
|
||||
"
|
||||
import { defineConfig } from 'cypress';
|
||||
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||
const myConfig = {
|
||||
...nxE2EPreset(__filename),
|
||||
video: false
|
||||
}
|
||||
|
||||
export default defineConfig({
|
||||
e2e: {
|
||||
...myConfig,
|
||||
/**
|
||||
* TODO(@nrwl/cypress): In Cypress v12,the testIsolation option is turned on by default.
|
||||
* This can cause tests to start breaking where not indended.
|
||||
* You should consider enabling this once you verify tests do not depend on each other
|
||||
* More Info: https://docs.cypress.io/guides/references/migration-guide#Test-Isolation
|
||||
**/
|
||||
testIsolation: false,
|
||||
},
|
||||
})
|
||||
"
|
||||
`);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function addCypressProject(tree: Tree, name: string) {
|
||||
const targets = {
|
||||
e2e: {
|
||||
executor: '@nrwl/cypress:cypress',
|
||||
options: {
|
||||
tsConfig: `apps/${name}/tsconfig.e2e.json`,
|
||||
testingType: 'e2e',
|
||||
browser: 'chrome',
|
||||
},
|
||||
configurations: {
|
||||
dev: {
|
||||
cypressConfig: `apps/${name}/cypress.config.ts`,
|
||||
devServerTarget: 'client:serve:dev',
|
||||
baseUrl: 'http://localhost:4206',
|
||||
},
|
||||
watch: {
|
||||
cypressConfig: 'apps/client-e2e/cypress-custom.config.ts',
|
||||
devServerTarget: 'client:serve:watch',
|
||||
baseUrl: 'http://localhost:4204',
|
||||
},
|
||||
},
|
||||
defaultConfiguration: 'dev',
|
||||
},
|
||||
};
|
||||
addProjectConfiguration(tree, name, {
|
||||
root: `apps/${name}`,
|
||||
sourceRoot: `apps/${name}/src`,
|
||||
projectType: 'application',
|
||||
targets,
|
||||
});
|
||||
// testIsolation
|
||||
tree.write(
|
||||
`apps/${name}/cypress.config.ts`,
|
||||
`import { defineConfig } from 'cypress';
|
||||
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||
|
||||
export default defineConfig({
|
||||
e2e: nxE2EPreset(__filename)
|
||||
})`
|
||||
);
|
||||
// test Cypress.Commands.Override
|
||||
tree.write(
|
||||
`apps/${name}/src/support/commands.ts`,
|
||||
`declare namespace Cypress {
|
||||
interface Chainable<Subject> {
|
||||
login(email: string, password: string): void;
|
||||
}
|
||||
}
|
||||
//
|
||||
// -- This is a parent command --
|
||||
Cypress.Commands.add('login', (email, password) => {
|
||||
console.log('Custom command example: Login', email, password);
|
||||
});
|
||||
Cypress.Commands.overwrite('find', () => {});
|
||||
`
|
||||
);
|
||||
// test .should(() => cy.<cmd>)
|
||||
tree.write(
|
||||
`apps/${name}/src/e2e/callback.spec.ts`,
|
||||
`describe('something', () => {
|
||||
it('should do the thing', () => {
|
||||
Cypress.Cookies.defaults()
|
||||
cy.server()
|
||||
|
||||
cy.wait('@getApi')
|
||||
.its('url').should('include', 'api/v1')
|
||||
cy.should((b) => {
|
||||
const a = 123;
|
||||
// I'm not doing nested cy stuff
|
||||
});
|
||||
cy.should(($s) => {
|
||||
cy.task("");
|
||||
})
|
||||
cy.should(function($el) {
|
||||
cy.task("");
|
||||
})
|
||||
})
|
||||
})`
|
||||
);
|
||||
tree.write(
|
||||
`apps/${name}/src/e2e/intercept-session.spec.ts`,
|
||||
`describe('something', () => {
|
||||
it('should do the thing', () => {
|
||||
Cypress.Cookies.defaults()
|
||||
Cypress.Cookies.preserveOnce('seesion_id', 'remember-token');
|
||||
Cypress.blah.abc()
|
||||
Cypress.Server.defaults({
|
||||
delay: 500,
|
||||
method: 'GET',
|
||||
})
|
||||
cy.server()
|
||||
cy.route(/api/, () => {
|
||||
return {
|
||||
'test': 'We’ll',
|
||||
}
|
||||
}).as('getApi')
|
||||
|
||||
cy.visit('/index.html')
|
||||
cy.window().then((win) => {
|
||||
const xhr = new win.XMLHttpRequest
|
||||
xhr.open('GET', '/api/v1/foo/bar?a=42')
|
||||
xhr.send()
|
||||
})
|
||||
|
||||
cy.wait('@getApi')
|
||||
.its('url').should('include', 'api/v1')
|
||||
})
|
||||
})`
|
||||
);
|
||||
tree.write(
|
||||
`apps/${name}/src/e2e/combo.spec.ts`,
|
||||
`describe('something', () => {
|
||||
it('should do the thing', () => {
|
||||
Cypress.Cookies.defaults()
|
||||
Cypress.Cookies.preserveOnce('seesion_id', 'remember-token');
|
||||
Cypress.blah.abc()
|
||||
Cypress.Server.defaults({
|
||||
delay: 500,
|
||||
method: 'GET',
|
||||
})
|
||||
cy.server()
|
||||
cy.route(/api/, () => {
|
||||
return {
|
||||
'test': 'We’ll',
|
||||
}
|
||||
}).as('getApi')
|
||||
|
||||
cy.visit('/index.html')
|
||||
cy.window().then((win) => {
|
||||
const xhr = new win.XMLHttpRequest
|
||||
xhr.open('GET', '/api/v1/foo/bar?a=42')
|
||||
xhr.send()
|
||||
})
|
||||
|
||||
cy.wait('@getApi')
|
||||
.its('url').should('include', 'api/v1')
|
||||
cy.should(($s) => {
|
||||
cy.get('@table').find('tr').should('have.length', 3)
|
||||
})
|
||||
})
|
||||
})`
|
||||
);
|
||||
}
|
||||
|
||||
function assertMigration(tree: Tree, name: string) {
|
||||
expect(tree.read(`apps/${name}/cypress.config.ts`, 'utf-8')).toContain(
|
||||
'testIsolation: false'
|
||||
);
|
||||
// command overrides
|
||||
expect(tree.read(`apps/${name}/src/support/commands.ts`, 'utf-8')).toContain(
|
||||
'TODO(@nrwl/cypress): This command can no longer be overridden'
|
||||
);
|
||||
// test .should(() => cy.<cmd>)
|
||||
expect(tree.read(`apps/${name}/src/e2e/callback.spec.ts`, 'utf-8')).toContain(
|
||||
'TODO(@nrwl/cypress): Nesting Cypress commands in a should assertion now throws.'
|
||||
);
|
||||
// use cy.intercept, cy.session
|
||||
const interceptSessionSpec = tree.read(
|
||||
`apps/${name}/src/e2e/intercept-session.spec.ts`,
|
||||
'utf-8'
|
||||
);
|
||||
expect(interceptSessionSpec).toContain(
|
||||
'// TODO(@nrwl/cypress): this command has been removed, use cy.session instead. https://docs.cypress.io/guides/references/migration-guide#Command-Cypress-API-Changes'
|
||||
);
|
||||
expect(interceptSessionSpec).toContain(
|
||||
'// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults'
|
||||
);
|
||||
// intercept,session & callback
|
||||
expect(
|
||||
tree.read(`apps/${name}/src/e2e/combo.spec.ts`, 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
}
|
||||
@ -1,245 +0,0 @@
|
||||
import {
|
||||
GeneratorCallback,
|
||||
getProjects,
|
||||
installPackagesTask,
|
||||
stripIndents,
|
||||
Tree,
|
||||
updateJson,
|
||||
visitNotIgnoredFiles,
|
||||
} from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { tsquery } from '@phenomnomnominal/tsquery';
|
||||
import {
|
||||
CallExpression,
|
||||
isArrowFunction,
|
||||
isCallExpression,
|
||||
isFunctionExpression,
|
||||
isObjectLiteralExpression,
|
||||
PropertyAccessExpression,
|
||||
PropertyAssignment,
|
||||
} from 'typescript';
|
||||
import { CypressExecutorOptions } from '../../executors/cypress/cypress.impl';
|
||||
import { installedCypressVersion } from '../../utils/cypress-version';
|
||||
import { BANNED_COMMANDS, isAlreadyCommented } from './helpers';
|
||||
|
||||
const JS_TS_FILE_MATCHER = /\.[jt]sx?$/;
|
||||
|
||||
export function updateToCypress12(tree: Tree): GeneratorCallback {
|
||||
if (installedCypressVersion() < 11) {
|
||||
return;
|
||||
}
|
||||
const projects = getProjects(tree);
|
||||
|
||||
forEachExecutorOptions<CypressExecutorOptions>(
|
||||
tree,
|
||||
'@nrwl/cypress:cypress',
|
||||
(options, projectName, targetName, configName) => {
|
||||
if (!(options.cypressConfig && tree.exists(options.cypressConfig))) {
|
||||
return;
|
||||
}
|
||||
const projectConfig = projects.get(projectName);
|
||||
turnOffTestIsolation(tree, options.cypressConfig);
|
||||
|
||||
visitNotIgnoredFiles(tree, projectConfig.root, (filePath) => {
|
||||
if (!JS_TS_FILE_MATCHER.test(filePath)) {
|
||||
return;
|
||||
}
|
||||
shouldUseCyIntercept(tree, filePath);
|
||||
shouldUseCySession(tree, filePath);
|
||||
shouldNotUseCyInShouldCB(tree, filePath);
|
||||
shouldNotOverrideCommands(tree, filePath);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
console.warn(stripIndents`Cypress 12 has lots of breaking changes that might subtly break your tests.
|
||||
This migration marked known issues that need to be manually migrated,
|
||||
but there can still be runtime based errors that were not detected.
|
||||
Please consult the offical Cypress v12 migration guide for more info on these changes and the next steps.
|
||||
https://docs.cypress.io/guides/references/migration-guide
|
||||
`);
|
||||
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
json.devDependencies.cypress = '^12.2.0';
|
||||
return json;
|
||||
});
|
||||
|
||||
return () => {
|
||||
installPackagesTask(tree);
|
||||
};
|
||||
}
|
||||
|
||||
export function turnOffTestIsolation(tree: Tree, configPath: string) {
|
||||
const config = tree.read(configPath, 'utf-8');
|
||||
const isTestIsolationSet = tsquery.query<PropertyAssignment>(
|
||||
config,
|
||||
'ExportAssignment ObjectLiteralExpression > PropertyAssignment:has(Identifier[name="testIsolation"])'
|
||||
);
|
||||
|
||||
if (isTestIsolationSet.length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const testIsolationProperty = `/**
|
||||
* TODO(@nrwl/cypress): In Cypress v12,the testIsolation option is turned on by default.
|
||||
* This can cause tests to start breaking where not indended.
|
||||
* You should consider enabling this once you verify tests do not depend on each other
|
||||
* More Info: https://docs.cypress.io/guides/references/migration-guide#Test-Isolation
|
||||
**/
|
||||
testIsolation: false,`;
|
||||
const updated = tsquery.replace(
|
||||
config,
|
||||
'ExportAssignment ObjectLiteralExpression > PropertyAssignment:has(Identifier[name="e2e"])',
|
||||
(node: PropertyAssignment) => {
|
||||
if (isObjectLiteralExpression(node.initializer)) {
|
||||
const listOfProperties = node.initializer.properties
|
||||
.map((j) => j.getText())
|
||||
.join(',\n ');
|
||||
return `e2e: {
|
||||
${listOfProperties},
|
||||
${testIsolationProperty}
|
||||
}`;
|
||||
}
|
||||
return `e2e: {
|
||||
...${node.initializer.getText()},
|
||||
${testIsolationProperty}
|
||||
}`;
|
||||
}
|
||||
);
|
||||
|
||||
tree.write(configPath, updated);
|
||||
}
|
||||
|
||||
/**
|
||||
* Leave a comment on all apis that have been removed andsuperseded by cy.intercept
|
||||
* stating they these API are now removed and need to update.
|
||||
* cy.route, cy.server, Cypress.Server.defaults
|
||||
**/
|
||||
export function shouldUseCyIntercept(tree: Tree, filePath: string) {
|
||||
const content = tree.read(filePath, 'utf-8');
|
||||
const markedRemovedCommands = tsquery.replace(
|
||||
content,
|
||||
':matches(PropertyAccessExpression:has(Identifier[name="cy"]):has(Identifier[name="server"], Identifier[name="route"]), PropertyAccessExpression:has(Identifier[name="defaults"]):has(Identifier[name="Cypress"], Identifier[name="Server"]))',
|
||||
(node: PropertyAccessExpression) => {
|
||||
if (isAlreadyCommented(node)) {
|
||||
return;
|
||||
}
|
||||
const expression = node.expression.getText().trim();
|
||||
// prevent extra chaining i.e. cy.route().as() will return 2 results
|
||||
// cy.route and cy.route().as
|
||||
// only need the first 1 so skip any extra chaining
|
||||
if (expression === 'cy' || expression === 'Cypress.Server') {
|
||||
return `// TODO(@nrwl/cypress): this command has been removed, use cy.intercept instead. https://docs.cypress.io/guides/references/migration-guide#cy-server-cy-route-and-Cypress-Server-defaults
|
||||
${node.getText()}`;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
tree.write(filePath, markedRemovedCommands);
|
||||
}
|
||||
|
||||
/**
|
||||
* Leave a comment on all apis that have been removed and superseded by cy.session
|
||||
* stating they these API are now removed and need to update.
|
||||
* Cypress.Cookies.defaults & Cypress.Cookies.preserveOnce
|
||||
**/
|
||||
export function shouldUseCySession(tree: Tree, filePath: string) {
|
||||
const content = tree.read(filePath, 'utf-8');
|
||||
const markedRemovedCommands = tsquery.replace(
|
||||
content,
|
||||
':matches(PropertyAccessExpression:has(Identifier[name="defaults"]):has(Identifier[name="Cypress"], Identifier[name="Cookies"]), PropertyAccessExpression:has(Identifier[name="preserveOnce"]):has(Identifier[name="Cypress"], Identifier[name="Cookies"]))',
|
||||
(node: PropertyAccessExpression) => {
|
||||
if (isAlreadyCommented(node)) {
|
||||
return;
|
||||
}
|
||||
const expression = node.expression.getText().trim();
|
||||
// prevent grabbing other Cypress.<something>.defaults
|
||||
if (expression === 'Cypress.Cookies') {
|
||||
return `// TODO(@nrwl/cypress): this command has been removed, use cy.session instead. https://docs.cypress.io/guides/references/migration-guide#Command-Cypress-API-Changes
|
||||
${node.getText()}`;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
tree.write(filePath, markedRemovedCommands);
|
||||
}
|
||||
|
||||
/**
|
||||
* leave a comment about nested cy commands in a cy.should callback
|
||||
* */
|
||||
export function shouldNotUseCyInShouldCB(tree: Tree, filePath: string) {
|
||||
const content = tree.read(filePath, 'utf-8');
|
||||
const markedNestedCyCommands = tsquery.replace(
|
||||
content,
|
||||
'CallExpression > PropertyAccessExpression:has(Identifier[name="cy"]):has(Identifier[name="should"])',
|
||||
(node: PropertyAccessExpression) => {
|
||||
if (
|
||||
isAlreadyCommented(node) ||
|
||||
(node.parent && !isCallExpression(node.parent))
|
||||
) {
|
||||
return;
|
||||
}
|
||||
const parentExpression = node.parent as CallExpression;
|
||||
if (
|
||||
parentExpression?.arguments?.[0] &&
|
||||
(isArrowFunction(parentExpression.arguments[0]) ||
|
||||
isFunctionExpression(parentExpression.arguments[0]))
|
||||
) {
|
||||
const isUsingNestedCyCommand =
|
||||
tsquery.query<PropertyAccessExpression>(
|
||||
parentExpression.arguments[0],
|
||||
'CallExpression > PropertyAccessExpression:has(Identifier[name="cy"])'
|
||||
)?.length > 0;
|
||||
if (isUsingNestedCyCommand) {
|
||||
return `/**
|
||||
* TODO(@nrwl/cypress): Nesting Cypress commands in a should assertion now throws.
|
||||
* You should use .then() to chain commands instead.
|
||||
* More Info: https://docs.cypress.io/guides/references/migration-guide#-should
|
||||
**/
|
||||
${node.getText()}`;
|
||||
}
|
||||
return node.getText();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
tree.write(filePath, markedNestedCyCommands);
|
||||
}
|
||||
|
||||
/**
|
||||
* leave a comment on all usages of overriding built-ins that are now banned
|
||||
* */
|
||||
export function shouldNotOverrideCommands(tree: Tree, filePath: string) {
|
||||
const content = tree.read(filePath, 'utf-8');
|
||||
const markedOverrideUsage = tsquery.replace(
|
||||
content,
|
||||
'PropertyAccessExpression:has(Identifier[name="overwrite"]):has(Identifier[name="Cypress"])',
|
||||
(node: PropertyAccessExpression) => {
|
||||
if (isAlreadyCommented(node)) {
|
||||
return;
|
||||
}
|
||||
const expression = node.expression.getText().trim();
|
||||
// prevent grabbing other Cypress.<something>.defaults
|
||||
|
||||
if (expression === 'Cypress.Commands') {
|
||||
// get value.
|
||||
const overwriteExpression = node.parent as CallExpression;
|
||||
|
||||
const command = (overwriteExpression.arguments?.[0] as any)?.text; // need string without quotes
|
||||
if (BANNED_COMMANDS.includes(command)) {
|
||||
// overwrite
|
||||
return `/**
|
||||
* TODO(@nrwl/cypress): This command can no longer be overridden
|
||||
* Consider using a different name like 'custom_${command}'
|
||||
* More info: https://docs.cypress.io/guides/references/migration-guide#Cypress-Commands-overwrite
|
||||
**/
|
||||
${node.getText()}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
tree.write(filePath, markedOverrideUsage);
|
||||
}
|
||||
|
||||
export default updateToCypress12;
|
||||
@ -20,42 +20,6 @@
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
"15.0.0": {
|
||||
"version": "15.0.0-beta.0",
|
||||
"packages": {
|
||||
"detox": {
|
||||
"version": "19.12.5",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"15.2.2": {
|
||||
"version": "15.2.2-beta.0",
|
||||
"packages": {
|
||||
"detox": {
|
||||
"version": "20.0.3",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"15.6.2": {
|
||||
"version": "15.6.2-beta.0",
|
||||
"packages": {
|
||||
"detox": {
|
||||
"version": "20.1.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"15.8.6": {
|
||||
"version": "15.8.6-beta.0",
|
||||
"packages": {
|
||||
"detox": {
|
||||
"version": "~20.5.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"16.0.0": {
|
||||
"version": "16.0.0-beta.3",
|
||||
"packages": {
|
||||
|
||||
@ -17,9 +17,15 @@
|
||||
],
|
||||
"patterns": [
|
||||
{
|
||||
"group": ["nx/**/*"],
|
||||
"message": "Use requireNx() from packages/devkit/nx.ts OR use a type import instead.",
|
||||
"allowTypeImports": true
|
||||
"group": [
|
||||
"nx/bin/*",
|
||||
"nx/src/*",
|
||||
"nx/plugins/*",
|
||||
"!nx/src/devkit-internals",
|
||||
"!nx/src/devkit-exports"
|
||||
],
|
||||
"allowTypeImports": true,
|
||||
"message": "Only import from nx/src/devkit-internals or nx/src/devkit-exports"
|
||||
},
|
||||
{
|
||||
"group": ["@nx/devkit/**/*"],
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
// After Nx v19, this can be removed and replaced with either:
|
||||
// - import {} from 'nx/src/devkit-exports'
|
||||
// - import {} from 'nx/src/devkit-internals'
|
||||
export function requireNx(): typeof import('nx/src/devkit-exports') &
|
||||
Partial<typeof import('nx/src/devkit-internals')> {
|
||||
let result = { ...require('nx/src/devkit-exports') };
|
||||
try {
|
||||
result = {
|
||||
...result,
|
||||
// Remove in Nx v19, devkit should not support Nx v16.0.2 at that point.
|
||||
...require('nx/src/devkit-internals'),
|
||||
};
|
||||
} catch {}
|
||||
return result;
|
||||
}
|
||||
@ -38,7 +38,7 @@
|
||||
"minimatch": "9.0.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"nx": ">= 16 <= 19"
|
||||
"nx": ">= 17 <= 20"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@ -1,15 +1,10 @@
|
||||
import type { Target } from 'nx/src/command-line/run/run';
|
||||
import type { ProjectGraph } from 'nx/src/config/project-graph';
|
||||
import type { ExecutorContext } from 'nx/src/devkit-exports';
|
||||
|
||||
import { requireNx } from '../../nx';
|
||||
|
||||
let { readCachedProjectGraph, splitTarget, splitByColons } = requireNx();
|
||||
|
||||
// TODO: Remove this in Nx 19 when Nx 16.7.0 is no longer supported
|
||||
splitTarget = splitTarget ?? require('nx/src/utils/split-target').splitTarget;
|
||||
splitByColons =
|
||||
splitByColons ?? ((s: string) => s.split(':') as [string, ...string[]]);
|
||||
import {
|
||||
ExecutorContext,
|
||||
ProjectGraph,
|
||||
readCachedProjectGraph,
|
||||
Target,
|
||||
} from 'nx/src/devkit-exports';
|
||||
import { splitByColons, splitTarget } from 'nx/src/devkit-internals';
|
||||
|
||||
/**
|
||||
* @deprecated(v17) A project graph should be passed to parseTargetString for best accuracy.
|
||||
|
||||
@ -1,19 +1,12 @@
|
||||
import type { Target } from 'nx/src/command-line/run/run';
|
||||
import type { ExecutorContext } from 'nx/src/config/misc-interfaces';
|
||||
import { requireNx } from '../../nx';
|
||||
import { relative } from 'path';
|
||||
|
||||
let {
|
||||
Workspaces,
|
||||
getExecutorInformation,
|
||||
import type { ExecutorContext, Target } from 'nx/src/devkit-exports';
|
||||
|
||||
import {
|
||||
calculateDefaultProjectName,
|
||||
combineOptionsForExecutor,
|
||||
} = requireNx();
|
||||
|
||||
// TODO: Remove this in Nx 19 when Nx 16.7.0 is no longer supported
|
||||
combineOptionsForExecutor =
|
||||
combineOptionsForExecutor ??
|
||||
require('nx/src/utils/params').combineOptionsForExecutor;
|
||||
getExecutorInformation,
|
||||
} from 'nx/src/devkit-internals';
|
||||
|
||||
/**
|
||||
* Reads and combines options for a given target.
|
||||
@ -38,32 +31,20 @@ export function readTargetOptions<T = any>(
|
||||
throw new Error(`Unable to find target ${target} for project ${project}`);
|
||||
}
|
||||
|
||||
// TODO(v19): remove Workspaces.
|
||||
const ws = new Workspaces(context.root);
|
||||
const [nodeModule, executorName] = targetConfiguration.executor.split(':');
|
||||
const { schema } = getExecutorInformation
|
||||
? getExecutorInformation(
|
||||
nodeModule,
|
||||
executorName,
|
||||
context.root,
|
||||
context.projectsConfigurations?.projects ?? context.workspace.projects
|
||||
)
|
||||
: // TODO(v19): remove readExecutor. This is to be backwards compatible with Nx 16.5 and below.
|
||||
(ws as any).readExecutor(nodeModule, executorName);
|
||||
const { schema } = getExecutorInformation(
|
||||
nodeModule,
|
||||
executorName,
|
||||
context.root,
|
||||
context.projectsConfigurations?.projects ?? context.workspace.projects
|
||||
);
|
||||
|
||||
const defaultProject = calculateDefaultProjectName
|
||||
? calculateDefaultProjectName(
|
||||
context.cwd,
|
||||
context.root,
|
||||
{ version: 2, projects: context.projectsConfigurations.projects },
|
||||
context.nxJsonConfiguration
|
||||
)
|
||||
: // TODO(v19): remove calculateDefaultProjectName. This is to be backwards compatible with Nx 16.5 and below.
|
||||
(ws as any).calculateDefaultProjectName(
|
||||
context.cwd,
|
||||
{ version: 2, projects: context.projectsConfigurations.projects },
|
||||
context.nxJsonConfiguration
|
||||
);
|
||||
const defaultProject = calculateDefaultProjectName(
|
||||
context.cwd,
|
||||
context.root,
|
||||
{ version: 2, projects: context.projectsConfigurations.projects },
|
||||
context.nxJsonConfiguration
|
||||
);
|
||||
|
||||
return combineOptionsForExecutor(
|
||||
{},
|
||||
|
||||
@ -1,7 +1,4 @@
|
||||
import type { Tree } from 'nx/src/devkit-exports';
|
||||
import { requireNx } from '../../nx';
|
||||
|
||||
const { readNxJson, updateNxJson } = requireNx();
|
||||
import { readNxJson, Tree, updateNxJson } from 'nx/src/devkit-exports';
|
||||
|
||||
export function addBuildTargetDefaults(
|
||||
tree: Tree,
|
||||
|
||||
@ -1,20 +1,22 @@
|
||||
import { prompt } from 'enquirer';
|
||||
import type { ProjectConfiguration } from 'nx/src/config/workspace-json-project-json';
|
||||
import type { Tree } from 'nx/src/generators/tree';
|
||||
import { join, relative } from 'path';
|
||||
import { requireNx } from '../../nx';
|
||||
import { names } from '../utils/names';
|
||||
|
||||
const {
|
||||
createProjectRootMappingsFromProjectConfigurations,
|
||||
findProjectForPath,
|
||||
import {
|
||||
getProjects,
|
||||
joinPathFragments,
|
||||
logger,
|
||||
normalizePath,
|
||||
output,
|
||||
ProjectConfiguration,
|
||||
Tree,
|
||||
workspaceRoot,
|
||||
} = requireNx();
|
||||
} from 'nx/src/devkit-exports';
|
||||
|
||||
import {
|
||||
createProjectRootMappingsFromProjectConfigurations,
|
||||
findProjectForPath,
|
||||
} from 'nx/src/devkit-internals';
|
||||
|
||||
export type NameAndDirectoryFormat = 'as-provided' | 'derived';
|
||||
export type ArtifactGenerationOptions = {
|
||||
@ -142,7 +144,7 @@ function logDeprecationMessage(
|
||||
formats: NameAndDirectoryFormats
|
||||
) {
|
||||
logger.warn(`
|
||||
In Nx 19, generating a ${options.artifactType} will no longer support providing a project and deriving the directory.
|
||||
In Nx 20, generating a ${options.artifactType} will no longer support providing a project and deriving the directory.
|
||||
Please provide the exact directory in the future.
|
||||
Example: nx g ${options.callingGenerator} ${formats['derived'].artifactName} --directory ${formats['derived'].directory}
|
||||
NOTE: The example above assumes the command is being run from the workspace root. If the command is being run from a subdirectory, the directory option should be adjusted accordingly.
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import type { Tree } from 'nx/src/generators/tree';
|
||||
import type { ProjectGraph } from 'nx/src/config/project-graph';
|
||||
import type { ProjectConfiguration } from 'nx/src/config/workspace-json-project-json';
|
||||
import { requireNx } from '../../nx';
|
||||
|
||||
const { getProjects } = requireNx();
|
||||
import {
|
||||
getProjects,
|
||||
ProjectConfiguration,
|
||||
ProjectGraph,
|
||||
Tree,
|
||||
} from 'nx/src/devkit-exports';
|
||||
|
||||
type CallBack<T> = (
|
||||
currentValue: T,
|
||||
|
||||
@ -1,13 +1,8 @@
|
||||
import type { Tree } from 'nx/src/generators/tree';
|
||||
import * as path from 'path';
|
||||
import type * as Prettier from 'prettier';
|
||||
import { requireNx } from '../../nx';
|
||||
|
||||
let { updateJson, readJson, sortObjectByKeys } = requireNx();
|
||||
|
||||
// TODO: Remove this in Nx 19 when Nx 16.7.0 is no longer supported
|
||||
sortObjectByKeys =
|
||||
sortObjectByKeys ?? require('nx/src/utils/object-sort').sortObjectByKeys;
|
||||
import { readJson, Tree, updateJson } from 'nx/src/devkit-exports';
|
||||
import { sortObjectByKeys } from 'nx/src/devkit-internals';
|
||||
|
||||
/**
|
||||
* Formats all the created or updated files using Prettier
|
||||
|
||||
@ -1,10 +1,8 @@
|
||||
import { readFileSync, readdirSync, statSync } from 'fs';
|
||||
import { readdirSync, readFileSync, statSync } from 'fs';
|
||||
import * as path from 'path';
|
||||
import type { Tree } from 'nx/src/generators/tree';
|
||||
import { requireNx } from '../../nx';
|
||||
import { isBinaryPath } from '../utils/binary-extensions';
|
||||
|
||||
const { logger } = requireNx();
|
||||
import { logger, Tree } from 'nx/src/devkit-exports';
|
||||
|
||||
/**
|
||||
* Generates a folder of files based on provided templates.
|
||||
|
||||
@ -1,34 +1,30 @@
|
||||
import type { TargetConfiguration } from 'nx/src/config/workspace-json-project-json';
|
||||
import type {
|
||||
ExpandedPluginConfiguration,
|
||||
NxJsonConfiguration,
|
||||
} from 'nx/src/config/nx-json';
|
||||
import type { Tree } from 'nx/src/generators/tree';
|
||||
import type {
|
||||
CreateNodes,
|
||||
CreateNodesContext,
|
||||
} from 'nx/src/project-graph/plugins';
|
||||
import type { ProjectGraph } from 'nx/src/config/project-graph';
|
||||
import type { RunCommandsOptions } from 'nx/src/executors/run-commands/run-commands.impl';
|
||||
import type { ConfigurationResult } from 'nx/src/project-graph/utils/project-configuration-utils';
|
||||
|
||||
import { minimatch } from 'minimatch';
|
||||
|
||||
import { forEachExecutorOptions } from '../executor-options-utils';
|
||||
import { deleteMatchingProperties } from './plugin-migration-utils';
|
||||
import { requireNx } from '../../../nx';
|
||||
|
||||
const {
|
||||
glob,
|
||||
import {
|
||||
readNxJson,
|
||||
updateNxJson,
|
||||
mergeTargetConfigurations,
|
||||
updateProjectConfiguration,
|
||||
readProjectConfiguration,
|
||||
ProjectGraph,
|
||||
ExpandedPluginConfiguration,
|
||||
NxJsonConfiguration,
|
||||
TargetConfiguration,
|
||||
Tree,
|
||||
CreateNodes,
|
||||
} from 'nx/src/devkit-exports';
|
||||
|
||||
import {
|
||||
mergeTargetConfigurations,
|
||||
retrieveProjectConfigurations,
|
||||
LoadedNxPlugin,
|
||||
ProjectConfigurationsError,
|
||||
} = requireNx();
|
||||
} from 'nx/src/devkit-internals';
|
||||
import type { ConfigurationResult } from 'nx/src/project-graph/utils/project-configuration-utils';
|
||||
|
||||
type PluginOptionsBuilder<T> = (targetName: string) => T;
|
||||
type PostTargetTransformer = (
|
||||
|
||||
@ -1,23 +1,22 @@
|
||||
import { prompt } from 'enquirer';
|
||||
import type { ProjectType } from 'nx/src/config/workspace-json-project-json';
|
||||
import type { Tree } from 'nx/src/generators/tree';
|
||||
import { join, relative } from 'path';
|
||||
import { requireNx } from '../../nx';
|
||||
import {
|
||||
extractLayoutDirectory,
|
||||
getWorkspaceLayout,
|
||||
} from '../utils/get-workspace-layout';
|
||||
import { names } from '../utils/names';
|
||||
|
||||
const {
|
||||
import {
|
||||
joinPathFragments,
|
||||
logger,
|
||||
normalizePath,
|
||||
output,
|
||||
ProjectType,
|
||||
readJson,
|
||||
stripIndents,
|
||||
Tree,
|
||||
workspaceRoot,
|
||||
} = requireNx();
|
||||
} from 'nx/src/devkit-exports';
|
||||
|
||||
export type ProjectNameAndRootFormat = 'as-provided' | 'derived';
|
||||
export type ProjectGenerationOptions = {
|
||||
@ -130,7 +129,7 @@ function logDeprecationMessage(
|
||||
formats: ProjectNameAndRootFormats
|
||||
) {
|
||||
logger.warn(stripIndents`
|
||||
In Nx 19, generating projects will no longer derive the name and root.
|
||||
In Nx 20, generating projects will no longer derive the name and root.
|
||||
Please provide the exact project name and root in the future.
|
||||
Example: nx g ${callingGenerator} ${formats['derived'].projectName} --directory ${formats['derived'].projectRoot}
|
||||
`);
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { GeneratorCallback } from 'nx/src/config/misc-interfaces';
|
||||
import type { GeneratorCallback } from 'nx/src/devkit-exports';
|
||||
|
||||
/**
|
||||
* Run tasks in serial
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { Tree } from 'nx/src/generators/tree';
|
||||
import type { Tree } from 'nx/src/devkit-exports';
|
||||
import type { ScriptTarget, ModuleKind } from 'typescript';
|
||||
import { typescriptVersion } from '../utils/versions';
|
||||
import { ensurePackage } from '../utils/package-json';
|
||||
|
||||
@ -1,7 +1,4 @@
|
||||
import type { Tree } from 'nx/src/generators/tree';
|
||||
import { requireNx } from '../../nx';
|
||||
|
||||
const { updateJson } = requireNx();
|
||||
import { Tree, updateJson } from 'nx/src/devkit-exports';
|
||||
|
||||
export function updateTsConfigsToJs(
|
||||
tree: Tree,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { Tree } from 'nx/src/generators/tree';
|
||||
import type { Tree } from 'nx/src/devkit-exports';
|
||||
import ignore from 'ignore';
|
||||
import { join, relative, sep } from 'path';
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import type { Tree } from 'nx/src/generators/tree';
|
||||
import type { Tree } from 'nx/src/devkit-exports';
|
||||
import { formatFiles } from '../../generators/format-files';
|
||||
import { replaceNrwlPackageWithNxPackage } from '../../utils/replace-package';
|
||||
|
||||
|
||||
@ -1,16 +1,13 @@
|
||||
import type { Tree } from 'nx/src/generators/tree';
|
||||
import type { GeneratorCallback } from 'nx/src/config/misc-interfaces';
|
||||
import type { Node, SyntaxKind } from 'typescript';
|
||||
import { visitNotIgnoredFiles } from '../../generators/visit-not-ignored-files';
|
||||
import { formatFiles } from '../../generators/format-files';
|
||||
import { requireNx } from '../../../nx';
|
||||
import {
|
||||
addDependenciesToPackageJson,
|
||||
ensurePackage,
|
||||
} from '../../utils/package-json';
|
||||
import { typescriptVersion } from '../../utils/versions';
|
||||
|
||||
const { readJson } = requireNx();
|
||||
import { GeneratorCallback, readJson, Tree } from 'nx/src/devkit-exports';
|
||||
|
||||
let tsModule: typeof import('typescript');
|
||||
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import { type ExecSyncOptions, execSync } from 'child_process';
|
||||
import { execSync, type ExecSyncOptions } from 'child_process';
|
||||
import { join } from 'path';
|
||||
import { requireNx } from '../../nx';
|
||||
|
||||
import type { Tree } from 'nx/src/generators/tree';
|
||||
import type { PackageManager } from 'nx/src/utils/package-manager';
|
||||
const { detectPackageManager, getPackageManagerCommand, joinPathFragments } =
|
||||
requireNx();
|
||||
import {
|
||||
detectPackageManager,
|
||||
getPackageManagerCommand,
|
||||
joinPathFragments,
|
||||
PackageManager,
|
||||
Tree,
|
||||
} from 'nx/src/devkit-exports';
|
||||
|
||||
/**
|
||||
* Runs `npm install` or `yarn install`. It will skip running the install if
|
||||
|
||||
@ -1,24 +1,23 @@
|
||||
import {
|
||||
type CreateNodes,
|
||||
type ProjectConfiguration,
|
||||
type ProjectGraph,
|
||||
type Tree,
|
||||
} from 'nx/src/devkit-exports';
|
||||
import type { PackageJson } from 'nx/src/utils/package-json';
|
||||
import * as yargs from 'yargs-parser';
|
||||
import { requireNx } from '../../nx';
|
||||
|
||||
const {
|
||||
readJson,
|
||||
writeJson,
|
||||
readNxJson,
|
||||
updateNxJson,
|
||||
retrieveProjectConfigurations,
|
||||
LoadedNxPlugin,
|
||||
ProjectConfigurationsError,
|
||||
} = requireNx();
|
||||
|
||||
import type { ConfigurationResult } from 'nx/src/project-graph/utils/project-configuration-utils';
|
||||
import * as yargs from 'yargs-parser';
|
||||
|
||||
import {
|
||||
CreateNodes,
|
||||
ProjectConfiguration,
|
||||
ProjectGraph,
|
||||
readJson,
|
||||
readNxJson,
|
||||
Tree,
|
||||
updateNxJson,
|
||||
writeJson,
|
||||
} from 'nx/src/devkit-exports';
|
||||
import {
|
||||
LoadedNxPlugin,
|
||||
ProjectConfigurationsError,
|
||||
retrieveProjectConfigurations,
|
||||
} from 'nx/src/devkit-internals';
|
||||
|
||||
/**
|
||||
* Iterates through various forms of plugin options to find the one which does not conflict with the current graph
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import type { CreateNodesContext } from 'nx/src/devkit-exports';
|
||||
import { requireNx } from '../../nx';
|
||||
import { join } from 'path';
|
||||
const { hashWithWorkspaceContext, hashArray, hashObject } = requireNx();
|
||||
import { CreateNodesContext, hashArray } from 'nx/src/devkit-exports';
|
||||
|
||||
import { hashObject, hashWithWorkspaceContext } from 'nx/src/devkit-internals';
|
||||
|
||||
export function calculateHashForCreateNodes(
|
||||
projectRoot: string,
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import { dirname, extname, join, sep } from 'path';
|
||||
import { existsSync, readdirSync } from 'fs';
|
||||
import { requireNx } from '../../nx';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
|
||||
const { workspaceRoot, registerTsProject } = requireNx();
|
||||
import { workspaceRoot } from 'nx/src/devkit-exports';
|
||||
import { registerTsProject } from 'nx/src/devkit-internals';
|
||||
|
||||
export let dynamicImport = new Function(
|
||||
'modulePath',
|
||||
|
||||
@ -1,17 +1,17 @@
|
||||
import type { Observable } from 'rxjs';
|
||||
import type { Executor, ExecutorContext } from 'nx/src/config/misc-interfaces';
|
||||
import type { ProjectsConfigurations } from 'nx/src/devkit-exports';
|
||||
import type {
|
||||
Executor,
|
||||
ExecutorContext,
|
||||
ProjectsConfigurations,
|
||||
} from 'nx/src/devkit-exports';
|
||||
|
||||
import { requireNx } from '../../nx';
|
||||
import { NX_VERSION } from './package-json';
|
||||
import { lt } from 'semver';
|
||||
|
||||
const {
|
||||
Workspaces,
|
||||
import {
|
||||
readNxJsonFromDisk,
|
||||
retrieveProjectConfigurationsWithAngularProjects,
|
||||
readProjectConfigurationsFromRootMap,
|
||||
} = requireNx();
|
||||
retrieveProjectConfigurationsWithAngularProjects,
|
||||
} from 'nx/src/devkit-internals';
|
||||
|
||||
/**
|
||||
* Convert an Nx Executor into an Angular Devkit Builder
|
||||
@ -21,43 +21,35 @@ const {
|
||||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
||||
export function convertNxExecutor(executor: Executor) {
|
||||
const builderFunction = (options, builderContext) => {
|
||||
const workspaces = new Workspaces(builderContext.workspaceRoot);
|
||||
const nxJsonConfiguration = readNxJsonFromDisk
|
||||
? readNxJsonFromDisk(builderContext.workspaceRoot)
|
||||
: // TODO(v19): remove readNxJson. This is to be backwards compatible with Nx 16.5 and below.
|
||||
(workspaces as any).readNxJson();
|
||||
const nxJsonConfiguration = readNxJsonFromDisk(
|
||||
builderContext.workspaceRoot
|
||||
);
|
||||
|
||||
const promise = async () => {
|
||||
const projectsConfigurations: ProjectsConfigurations =
|
||||
retrieveProjectConfigurationsWithAngularProjects
|
||||
? {
|
||||
version: 2,
|
||||
projects: await retrieveProjectConfigurationsWithAngularProjects(
|
||||
builderContext.workspaceRoot,
|
||||
nxJsonConfiguration
|
||||
).then((p) => {
|
||||
if ((p as any).projectNodes) {
|
||||
return (p as any).projectNodes;
|
||||
}
|
||||
// v18.3.4 changed projects to be keyed by root
|
||||
// rather than project name
|
||||
if (lt(NX_VERSION, '18.3.4')) {
|
||||
return p.projects;
|
||||
}
|
||||
const projectsConfigurations: ProjectsConfigurations = {
|
||||
version: 2,
|
||||
projects: await retrieveProjectConfigurationsWithAngularProjects(
|
||||
builderContext.workspaceRoot,
|
||||
nxJsonConfiguration
|
||||
).then((p) => {
|
||||
if ((p as any).projectNodes) {
|
||||
return (p as any).projectNodes;
|
||||
}
|
||||
// v18.3.4 changed projects to be keyed by root
|
||||
// rather than project name
|
||||
if (lt(NX_VERSION, '18.3.4')) {
|
||||
return p.projects;
|
||||
}
|
||||
|
||||
if (readProjectConfigurationsFromRootMap) {
|
||||
return readProjectConfigurationsFromRootMap(p.projects);
|
||||
}
|
||||
if (readProjectConfigurationsFromRootMap) {
|
||||
return readProjectConfigurationsFromRootMap(p.projects);
|
||||
}
|
||||
|
||||
throw new Error(
|
||||
'Unable to successfully map Nx executor -> Angular Builder'
|
||||
);
|
||||
}),
|
||||
}
|
||||
: // TODO(v19): remove retrieveProjectConfigurations. This is to be backwards compatible with Nx 16.5 and below.
|
||||
(workspaces as any).readProjectsConfigurations({
|
||||
_includeProjectsFromAngularJson: true,
|
||||
});
|
||||
throw new Error(
|
||||
'Unable to successfully map Nx executor -> Angular Builder'
|
||||
);
|
||||
}),
|
||||
};
|
||||
|
||||
const context: ExecutorContext = {
|
||||
root: builderContext.workspaceRoot,
|
||||
|
||||
@ -1,15 +1,14 @@
|
||||
import { join } from 'path';
|
||||
import { existsSync } from 'fs';
|
||||
|
||||
import type {
|
||||
CreateNodesContext,
|
||||
ProjectConfiguration,
|
||||
} from 'nx/src/devkit-exports';
|
||||
import type { PackageJson } from 'nx/src/utils/package-json';
|
||||
import type { InputDefinition } from 'nx/src/config/workspace-json-project-json';
|
||||
import { requireNx } from '../../nx';
|
||||
|
||||
const { readJsonFile } = requireNx();
|
||||
import {
|
||||
CreateNodesContext,
|
||||
ProjectConfiguration,
|
||||
readJsonFile,
|
||||
} from 'nx/src/devkit-exports';
|
||||
|
||||
/**
|
||||
* Get the named inputs available for a directory
|
||||
|
||||
@ -1,7 +1,4 @@
|
||||
import type { Tree } from 'nx/src/generators/tree';
|
||||
import { requireNx } from '../../nx';
|
||||
|
||||
const { readNxJson } = requireNx();
|
||||
import { readNxJson, Tree } from 'nx/src/devkit-exports';
|
||||
|
||||
/**
|
||||
* Returns workspace defaults. It includes defaults folders for apps and libs,
|
||||
|
||||
@ -1,20 +1,14 @@
|
||||
import type {
|
||||
FileChange,
|
||||
Tree,
|
||||
TreeWriteOptions,
|
||||
} from 'nx/src/generators/tree';
|
||||
import type {
|
||||
Generator,
|
||||
GeneratorCallback,
|
||||
} from 'nx/src/config/misc-interfaces';
|
||||
import { join, relative } from 'path';
|
||||
import type { Mode } from 'fs';
|
||||
import { requireNx } from '../../nx';
|
||||
|
||||
let { logger, stripIndent } = requireNx();
|
||||
|
||||
// TODO: Remove this in Nx 19 when Nx 16.7.0 is no longer supported
|
||||
stripIndent = stripIndent ?? require('nx/src/utils/logger').stripIndent;
|
||||
import type { TreeWriteOptions } from 'nx/src/generators/tree';
|
||||
import {
|
||||
FileChange,
|
||||
Generator,
|
||||
GeneratorCallback,
|
||||
logger,
|
||||
Tree,
|
||||
} from 'nx/src/devkit-exports';
|
||||
import { stripIndent } from 'nx/src/devkit-internals';
|
||||
|
||||
class RunCallbackTask {
|
||||
constructor(private callback: GeneratorCallback) {}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { requireNx } from '../../nx';
|
||||
import { output } from 'nx/src/devkit-exports';
|
||||
|
||||
export function logShowProjectCommand(projectName: string): void {
|
||||
const { output } = requireNx();
|
||||
output.log({
|
||||
title: `👀 View Details of ${projectName}`,
|
||||
bodyLines: [
|
||||
|
||||
@ -1,9 +1,8 @@
|
||||
import type { Tree } from 'nx/src/generators/tree';
|
||||
import { relative } from 'path';
|
||||
import { visitNotIgnoredFiles } from '../generators/visit-not-ignored-files';
|
||||
import { requireNx } from '../../nx';
|
||||
|
||||
const { normalizePath } = requireNx();
|
||||
import { normalizePath, Tree } from 'nx/src/devkit-exports';
|
||||
|
||||
/**
|
||||
* Analogous to cp -r oldDir newDir
|
||||
*/
|
||||
|
||||
@ -1,27 +1,23 @@
|
||||
import { execSync } from 'child_process';
|
||||
import { Module } from 'module';
|
||||
|
||||
import type { Tree } from 'nx/src/generators/tree';
|
||||
|
||||
import type { GeneratorCallback } from 'nx/src/config/misc-interfaces';
|
||||
import { clean, coerce, gt } from 'semver';
|
||||
|
||||
import { installPackagesTask } from '../tasks/install-packages-task';
|
||||
import { requireNx } from '../../nx';
|
||||
import { dirSync } from 'tmp';
|
||||
import { join } from 'path';
|
||||
import type { PackageManager } from 'nx/src/utils/package-manager';
|
||||
import { writeFileSync } from 'fs';
|
||||
|
||||
const {
|
||||
readJson,
|
||||
updateJson,
|
||||
getPackageManagerCommand,
|
||||
workspaceRoot,
|
||||
import {
|
||||
detectPackageManager,
|
||||
createTempNpmDirectory,
|
||||
GeneratorCallback,
|
||||
getPackageManagerCommand,
|
||||
getPackageManagerVersion,
|
||||
} = requireNx();
|
||||
PackageManager,
|
||||
readJson,
|
||||
Tree,
|
||||
updateJson,
|
||||
workspaceRoot,
|
||||
} from 'nx/src/devkit-exports';
|
||||
import { createTempNpmDirectory } from 'nx/src/devkit-internals';
|
||||
import { writeFileSync } from 'fs';
|
||||
|
||||
const UNIDENTIFIED_VERSION = 'UNIDENTIFIED_VERSION';
|
||||
const NON_SEMVER_TAGS = {
|
||||
|
||||
@ -1,18 +1,16 @@
|
||||
import type { Tree } from 'nx/src/generators/tree';
|
||||
import {
|
||||
getProjects,
|
||||
logger,
|
||||
readNxJson,
|
||||
Tree,
|
||||
updateJson,
|
||||
updateNxJson,
|
||||
updateProjectConfiguration,
|
||||
} from 'nx/src/devkit-exports';
|
||||
import type { PackageJson } from 'nx/src/utils/package-json';
|
||||
import { requireNx } from '../../nx';
|
||||
import { visitNotIgnoredFiles } from '../generators/visit-not-ignored-files';
|
||||
import { basename } from 'path';
|
||||
import { isBinaryPath } from './binary-extensions';
|
||||
const { logger } = requireNx();
|
||||
|
||||
const {
|
||||
getProjects,
|
||||
updateProjectConfiguration,
|
||||
readNxJson,
|
||||
updateNxJson,
|
||||
updateJson,
|
||||
} = requireNx();
|
||||
|
||||
export function replaceNrwlPackageWithNxPackage(
|
||||
tree: Tree,
|
||||
|
||||
@ -1,19 +1,15 @@
|
||||
import type {
|
||||
Tree,
|
||||
import {
|
||||
CreateNodes,
|
||||
ProjectConfiguration,
|
||||
TargetConfiguration,
|
||||
} from 'nx/src/devkit-exports';
|
||||
import { requireNx } from '../../nx';
|
||||
const {
|
||||
readNxJson,
|
||||
updateNxJson,
|
||||
glob,
|
||||
hashObject,
|
||||
findProjectForPath,
|
||||
ProjectConfiguration,
|
||||
readNxJson,
|
||||
readProjectConfiguration,
|
||||
TargetConfiguration,
|
||||
Tree,
|
||||
updateNxJson,
|
||||
updateProjectConfiguration,
|
||||
} = requireNx();
|
||||
} from 'nx/src/devkit-exports';
|
||||
import { findProjectForPath, hashObject } from 'nx/src/devkit-internals';
|
||||
|
||||
export async function replaceProjectConfigurationsWithPlugin<T = unknown>(
|
||||
tree: Tree,
|
||||
|
||||
@ -1,11 +1,5 @@
|
||||
{
|
||||
"generators": {
|
||||
"set-generate-package-json": {
|
||||
"cli": "nx",
|
||||
"version": "15.8.7-beta.0",
|
||||
"description": "Set generatePackageJson to true to maintain existing behavior of generating package.json in output path.",
|
||||
"factory": "./src/migrations/update-15-8-7/set-generate-package-json"
|
||||
},
|
||||
"update-16-0-0-add-nx-packages": {
|
||||
"cli": "nx",
|
||||
"version": "16.0.0-beta.1",
|
||||
@ -20,15 +14,6 @@
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
"15.7.0": {
|
||||
"version": "15.7.0-beta.0",
|
||||
"packages": {
|
||||
"esbuild": {
|
||||
"version": "0.17.5",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"16.0.0": {
|
||||
"version": "16.0.0-beta.5",
|
||||
"packages": {
|
||||
|
||||
@ -1,73 +0,0 @@
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import update from './set-generate-package-json';
|
||||
|
||||
describe('Migration: Set generatePackageJson', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace();
|
||||
});
|
||||
|
||||
it('should keep existing generatePackageJson option if it exists', async () => {
|
||||
addProjectConfiguration(tree, 'myapp', {
|
||||
root: 'myapp',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/esbuild:esbuild',
|
||||
options: {
|
||||
generatePackageJson: false,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await update(tree);
|
||||
|
||||
const config = readProjectConfiguration(tree, 'myapp');
|
||||
|
||||
expect(config.targets.build.options).toEqual({
|
||||
generatePackageJson: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should set generatePackageJson to true for esbuild targets', async () => {
|
||||
addProjectConfiguration(tree, 'myapp', {
|
||||
root: 'myapp',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/esbuild:esbuild',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await update(tree);
|
||||
|
||||
const config = readProjectConfiguration(tree, 'myapp');
|
||||
|
||||
expect(config.targets.build.options).toEqual({
|
||||
generatePackageJson: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should ignore targets not using esbuild', async () => {
|
||||
addProjectConfiguration(tree, 'myapp', {
|
||||
root: 'myapp',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/webpack:webpack',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await update(tree);
|
||||
|
||||
const config = readProjectConfiguration(tree, 'myapp');
|
||||
|
||||
expect(config.targets.build.options).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@ -1,32 +0,0 @@
|
||||
import type { Tree } from '@nx/devkit';
|
||||
import {
|
||||
formatFiles,
|
||||
getProjects,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
|
||||
export default async function update(tree: Tree): Promise<void> {
|
||||
const projects = getProjects(tree);
|
||||
|
||||
projects.forEach((projectConfig, projectName) => {
|
||||
let shouldUpdate = false;
|
||||
|
||||
Object.entries(projectConfig.targets).forEach(
|
||||
([targetName, targetConfig]) => {
|
||||
if (targetConfig.executor === '@nrwl/esbuild:esbuild') {
|
||||
shouldUpdate = true;
|
||||
|
||||
projectConfig.targets[targetName].options ??= {};
|
||||
projectConfig.targets[targetName].options.generatePackageJson ??=
|
||||
true;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (shouldUpdate) {
|
||||
updateProjectConfiguration(tree, projectName, projectConfig);
|
||||
}
|
||||
});
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
@ -1,17 +1,5 @@
|
||||
{
|
||||
"generators": {
|
||||
"add-eslint-inputs": {
|
||||
"cli": "nx",
|
||||
"version": "15.0.0-beta.0",
|
||||
"description": "Stop hashing eslint config files for build targets and dependent tasks",
|
||||
"factory": "./src/migrations/update-15-0-0/add-eslint-inputs"
|
||||
},
|
||||
"add-eslint-ignore": {
|
||||
"cli": "nx",
|
||||
"version": "15.7.1-beta.0",
|
||||
"description": "Add node_modules to root eslint ignore",
|
||||
"factory": "./src/migrations/update-15-7-1/add-eslint-ignore"
|
||||
},
|
||||
"update-16-0-0-add-nx-packages": {
|
||||
"cli": "nx",
|
||||
"version": "16.0.0-beta.1",
|
||||
|
||||
@ -1,187 +0,0 @@
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import {
|
||||
Tree,
|
||||
addProjectConfiguration,
|
||||
readNxJson,
|
||||
updateNxJson,
|
||||
} from '@nx/devkit';
|
||||
import addEslintInputs from './add-eslint-inputs';
|
||||
import { ESLINT_CONFIG_FILENAMES } from '../../utils/config-file';
|
||||
|
||||
describe('15.0.0 migration (add-eslint-inputs)', () => {
|
||||
let tree: Tree;
|
||||
|
||||
describe('production', () => {
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
|
||||
updateNxJson(tree, {
|
||||
namedInputs: {
|
||||
default: ['{projectRoot}/**/*', 'sharedGlobals'],
|
||||
sharedGlobals: [],
|
||||
production: ['default'],
|
||||
},
|
||||
});
|
||||
|
||||
addProjectConfiguration(tree, 'proj', {
|
||||
root: 'proj',
|
||||
targets: {
|
||||
lint: {
|
||||
executor: '@nrwl/linter:eslint',
|
||||
options: {},
|
||||
},
|
||||
lint2: {
|
||||
executor: '@nrwl/linter:eslint',
|
||||
options: {},
|
||||
},
|
||||
notTest: {
|
||||
executor: 'nx:run-commands',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test.each(ESLINT_CONFIG_FILENAMES)(
|
||||
'should ignore %p for production',
|
||||
async (eslintConfigFilename) => {
|
||||
tree.write(eslintConfigFilename, '{}');
|
||||
|
||||
await addEslintInputs(tree);
|
||||
|
||||
const updated = readNxJson(tree);
|
||||
|
||||
expect(updated.namedInputs.production).toEqual([
|
||||
'default',
|
||||
`!{projectRoot}/${eslintConfigFilename}`,
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
||||
test.each(ESLINT_CONFIG_FILENAMES)(
|
||||
'should add %p to all lint targets',
|
||||
async (eslintConfigFilename) => {
|
||||
tree.write(eslintConfigFilename, '{}');
|
||||
|
||||
await addEslintInputs(tree);
|
||||
|
||||
const updated = readNxJson(tree);
|
||||
const result = ['default', `{workspaceRoot}/${eslintConfigFilename}`];
|
||||
|
||||
expect(updated.targetDefaults.lint.inputs).toEqual(result);
|
||||
expect(updated.targetDefaults.lint2.inputs).toEqual(result);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('development', () => {
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
|
||||
addProjectConfiguration(tree, 'proj', {
|
||||
root: 'proj',
|
||||
targets: {
|
||||
lint: {
|
||||
executor: '@nrwl/linter:eslint',
|
||||
options: {},
|
||||
},
|
||||
lint2: {
|
||||
executor: '@nrwl/linter:eslint',
|
||||
options: {},
|
||||
},
|
||||
notTest: {
|
||||
executor: 'nx:run-commands',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test.each(ESLINT_CONFIG_FILENAMES)(
|
||||
'should not add `!{projectRoot}/%s` if `workspaceConfiguration.namedInputs` is undefined',
|
||||
async (eslintConfigFilename) => {
|
||||
tree.write(eslintConfigFilename, '{}');
|
||||
|
||||
await addEslintInputs(tree);
|
||||
|
||||
const updated = readNxJson(tree);
|
||||
|
||||
expect(updated.namedInputs?.production).toBeUndefined();
|
||||
}
|
||||
);
|
||||
|
||||
test.each(ESLINT_CONFIG_FILENAMES)(
|
||||
'should not add `!{projectRoot}/%s` if `workspaceConfiguration.namedInputs.production` is undefined',
|
||||
async (eslintConfigFilename) => {
|
||||
updateNxJson(tree, {
|
||||
namedInputs: {},
|
||||
});
|
||||
|
||||
tree.write(eslintConfigFilename, '{}');
|
||||
|
||||
await addEslintInputs(tree);
|
||||
|
||||
const updated = readNxJson(tree);
|
||||
|
||||
expect(updated.namedInputs?.production).toBeUndefined();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
describe('lintTargetDefaults.input fallback values', () => {
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
|
||||
addProjectConfiguration(tree, 'proj', {
|
||||
root: 'proj',
|
||||
targets: {
|
||||
lint: {
|
||||
executor: '@nrwl/linter:eslint',
|
||||
options: {},
|
||||
},
|
||||
lint2: {
|
||||
executor: '@nrwl/linter:eslint',
|
||||
options: {},
|
||||
},
|
||||
notTest: {
|
||||
executor: 'nx:run-commands',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test.each(ESLINT_CONFIG_FILENAMES)(
|
||||
'should not override `targetDefaults.lint.inputs` with `%s` as there was a default target set in the workspace config',
|
||||
async (eslintConfigFilename) => {
|
||||
updateNxJson(tree, {
|
||||
targetDefaults: {
|
||||
lint: {
|
||||
inputs: ['{workspaceRoot}/.eslintrc.default'],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
tree.write(eslintConfigFilename, '{}');
|
||||
|
||||
await addEslintInputs(tree);
|
||||
|
||||
const updated = readNxJson(tree);
|
||||
|
||||
expect(updated.targetDefaults.lint.inputs).toEqual([
|
||||
'{workspaceRoot}/.eslintrc.default',
|
||||
]);
|
||||
expect(updated.targetDefaults.lint2.inputs).toEqual([
|
||||
'default',
|
||||
`{workspaceRoot}/${eslintConfigFilename}`,
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
||||
it('should return `default` if there is no globalEslintFile', async () => {
|
||||
await addEslintInputs(tree);
|
||||
|
||||
const updated = readNxJson(tree);
|
||||
|
||||
expect(updated.targetDefaults.lint.inputs).toEqual(['default']);
|
||||
expect(updated.targetDefaults.lint2.inputs).toEqual(['default']);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,42 +0,0 @@
|
||||
import {
|
||||
formatFiles,
|
||||
joinPathFragments,
|
||||
readNxJson,
|
||||
Tree,
|
||||
updateNxJson,
|
||||
} from '@nx/devkit';
|
||||
import { getEslintTargets } from '../../generators/utils/eslint-targets';
|
||||
import { ESLINT_CONFIG_FILENAMES } from '../../utils/config-file';
|
||||
|
||||
export default async function addEslintInputs(tree: Tree) {
|
||||
const nxJson = readNxJson(tree);
|
||||
|
||||
const globalEslintFile = ESLINT_CONFIG_FILENAMES.find((file) =>
|
||||
tree.exists(file)
|
||||
);
|
||||
|
||||
if (globalEslintFile && nxJson.namedInputs?.production) {
|
||||
const productionFileset = new Set(nxJson.namedInputs.production);
|
||||
|
||||
productionFileset.add(`!{projectRoot}/${globalEslintFile}`);
|
||||
|
||||
nxJson.namedInputs.production = Array.from(productionFileset);
|
||||
}
|
||||
|
||||
for (const targetName of getEslintTargets(tree)) {
|
||||
nxJson.targetDefaults ??= {};
|
||||
|
||||
const lintTargetDefaults = (nxJson.targetDefaults[targetName] ??= {});
|
||||
|
||||
lintTargetDefaults.inputs ??= [
|
||||
'default',
|
||||
...(globalEslintFile
|
||||
? [joinPathFragments('{workspaceRoot}', globalEslintFile)]
|
||||
: []),
|
||||
];
|
||||
}
|
||||
|
||||
updateNxJson(tree, nxJson);
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
@ -1,107 +0,0 @@
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
readJson,
|
||||
readNxJson,
|
||||
Tree,
|
||||
updateNxJson,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import addEslintIgnore from './add-eslint-ignore';
|
||||
|
||||
describe('15.7.1 migration (add-eslintignore)', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
|
||||
updateNxJson(tree, {
|
||||
namedInputs: {
|
||||
default: ['{projectRoot}/**/*', 'sharedGlobals'],
|
||||
sharedGlobals: [],
|
||||
},
|
||||
});
|
||||
|
||||
addProjectConfiguration(tree, 'proj', {
|
||||
root: 'proj',
|
||||
targets: {
|
||||
lint: {
|
||||
executor: '@nrwl/linter:eslint',
|
||||
options: {},
|
||||
},
|
||||
lint2: {
|
||||
executor: '@nrwl/linter:eslint',
|
||||
options: {},
|
||||
},
|
||||
notTest: {
|
||||
executor: 'nx:run-commands',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should not add .eslintignore if eslint config does not exist', async () => {
|
||||
await addEslintIgnore(tree);
|
||||
expect(tree.exists('.eslintignore')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should add .eslintignore if it does not exist', async () => {
|
||||
ensureGlobalConfig(tree);
|
||||
|
||||
await addEslintIgnore(tree);
|
||||
|
||||
expect(tree.exists('.eslintignore')).toBeTruthy();
|
||||
expect(tree.read('.eslintignore', 'utf-8')).toEqual('node_modules\n');
|
||||
});
|
||||
|
||||
it('should add node_modules if missing in .eslintignore', async () => {
|
||||
ensureGlobalConfig(tree);
|
||||
|
||||
const original = 'dist\ntmp\n';
|
||||
tree.write('.eslintignore', original);
|
||||
|
||||
await addEslintIgnore(tree);
|
||||
|
||||
expect(tree.read('.eslintignore', 'utf-8')).toEqual(
|
||||
`node_modules\n${original}`
|
||||
);
|
||||
});
|
||||
|
||||
it('should not add node_modules if already in .eslintignore', async () => {
|
||||
ensureGlobalConfig(tree);
|
||||
|
||||
const original = `dist\nnode_modules\ntmp\n`;
|
||||
tree.write('.eslintignore', original);
|
||||
|
||||
await addEslintIgnore(tree);
|
||||
|
||||
expect(tree.read('.eslintignore', 'utf-8')).toEqual(original);
|
||||
});
|
||||
|
||||
it('should add lint target', async () => {
|
||||
ensureGlobalConfig(tree);
|
||||
|
||||
await addEslintIgnore(tree);
|
||||
|
||||
expect(tree.exists('.eslintignore')).toBeTruthy();
|
||||
expect(readJson(tree, 'nx.json').targetDefaults).toMatchInlineSnapshot(`
|
||||
{
|
||||
"lint": {
|
||||
"inputs": [
|
||||
"default",
|
||||
"{workspaceRoot}/.eslintrc.json",
|
||||
],
|
||||
},
|
||||
"lint2": {
|
||||
"inputs": [
|
||||
"default",
|
||||
"{workspaceRoot}/.eslintrc.json",
|
||||
],
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
function ensureGlobalConfig(tree: Tree) {
|
||||
tree.write('.eslintrc.json', '{}');
|
||||
}
|
||||
@ -1,49 +0,0 @@
|
||||
import {
|
||||
formatFiles,
|
||||
joinPathFragments,
|
||||
readJson,
|
||||
Tree,
|
||||
updateNxJson,
|
||||
} from '@nx/devkit';
|
||||
import { getEslintTargets } from '../../generators/utils/eslint-targets';
|
||||
import { ESLINT_CONFIG_FILENAMES } from '../../utils/config-file';
|
||||
|
||||
export default async function addEslintIgnore(tree: Tree) {
|
||||
const nxJson = readJson(tree, 'nx.json');
|
||||
|
||||
const globalEslintFile = ESLINT_CONFIG_FILENAMES.find((file) =>
|
||||
tree.exists(file)
|
||||
);
|
||||
|
||||
if (globalEslintFile) {
|
||||
if (tree.exists('.eslintignore')) {
|
||||
const content = tree.read('.eslintignore', 'utf-8');
|
||||
if (!content.includes('node_modules')) {
|
||||
tree.write('.eslintignore', `node_modules\n${content}`);
|
||||
}
|
||||
} else {
|
||||
tree.write('.eslintignore', 'node_modules\n');
|
||||
}
|
||||
|
||||
for (const targetName of getEslintTargets(tree)) {
|
||||
nxJson.targetDefaults ??= {};
|
||||
const lintTargetDefaults = (nxJson.targetDefaults[targetName] ??= {});
|
||||
|
||||
const lintIgnorePath = joinPathFragments(
|
||||
'{workspaceRoot}',
|
||||
globalEslintFile
|
||||
);
|
||||
|
||||
if (lintTargetDefaults.inputs) {
|
||||
if (!lintTargetDefaults.inputs.includes(lintIgnorePath)) {
|
||||
lintTargetDefaults.inputs.push(lintIgnorePath);
|
||||
}
|
||||
} else {
|
||||
lintTargetDefaults.inputs = ['default', lintIgnorePath];
|
||||
}
|
||||
}
|
||||
|
||||
updateNxJson(tree, nxJson);
|
||||
await formatFiles(tree);
|
||||
}
|
||||
}
|
||||
@ -1,23 +1,5 @@
|
||||
{
|
||||
"generators": {
|
||||
"change-expo-jest-preset": {
|
||||
"version": "15.0.3-beta.0",
|
||||
"cli": "nx",
|
||||
"description": "Change jest config preset tof expo projects",
|
||||
"factory": "./src/migrations/update-15-0-3/change-jest-preset"
|
||||
},
|
||||
"add-new-expo-cli-targets": {
|
||||
"version": "15.0.3-beta.0",
|
||||
"cli": "nx",
|
||||
"description": "Add targets for @expo/cli and replace eject target",
|
||||
"factory": "./src/migrations/update-15-0-3/add-new-expo-cli-targets"
|
||||
},
|
||||
"change-webpack-to-metro": {
|
||||
"version": "15.8.3-beta.0",
|
||||
"cli": "nx",
|
||||
"description": "Change webpack to metro in expo projects",
|
||||
"factory": "./src/migrations/update-15-8-3/change-webpack-to-metro"
|
||||
},
|
||||
"remove-deprecated-expo-targets": {
|
||||
"version": "16.0.0-beta.0",
|
||||
"cli": "nx",
|
||||
@ -86,295 +68,6 @@
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
"15.0.0": {
|
||||
"version": "15.0.0-beta.0",
|
||||
"packages": {
|
||||
"metro-resolver": {
|
||||
"version": "0.72.3",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@testing-library/react-native": {
|
||||
"version": "11.2.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@testing-library/jest-native": {
|
||||
"version": "4.0.13",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo": {
|
||||
"version": "46.0.13",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-cli": {
|
||||
"version": "6.0.6",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native": {
|
||||
"version": "0.69.6",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"15.0.3": {
|
||||
"version": "15.0.3-beta.0",
|
||||
"packages": {
|
||||
"@testing-library/react-native": {
|
||||
"version": "11.3.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@testing-library/jest-native": {
|
||||
"version": "5.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo": {
|
||||
"version": "46.0.16",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-cli": {
|
||||
"version": "6.0.6",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"eas-cli": {
|
||||
"version": "2.4.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@expo/cli": {
|
||||
"version": "0.3.2",
|
||||
"alwaysAddToPackageJson": false,
|
||||
"addToPackageJson": "devDependencies"
|
||||
}
|
||||
}
|
||||
},
|
||||
"15.2.2": {
|
||||
"version": "15.2.2-beta.0",
|
||||
"packages": {
|
||||
"metro-resolver": {
|
||||
"version": "0.73.3",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"15.3.0": {
|
||||
"version": "15.3.0-beta.0",
|
||||
"packages": {
|
||||
"@testing-library/react-native": {
|
||||
"version": "11.5.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@testing-library/jest-native": {
|
||||
"version": "5.3.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo": {
|
||||
"version": "47.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@expo/metro-config": {
|
||||
"version": "0.5.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-splash-screen": {
|
||||
"version": "~0.17.5",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-cli": {
|
||||
"version": "6.0.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@expo/cli": {
|
||||
"version": "0.4.10",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"eas-cli": {
|
||||
"version": "2.8.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"babel-preset-expo": {
|
||||
"version": "~9.2.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native": {
|
||||
"version": "0.70.5",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@types/react-native": {
|
||||
"version": "0.70.7",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native-svg": {
|
||||
"version": "13.3.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@svgr/webpack": {
|
||||
"version": "^6.5.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"15.4.3": {
|
||||
"version": "15.4.3-beta.0",
|
||||
"packages": {
|
||||
"expo": {
|
||||
"version": "47.0.9",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@expo/metro-config": {
|
||||
"version": "0.5.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@expo/webpack-config": {
|
||||
"version": "~0.17.3",
|
||||
"alwaysAddToPackageJson": false,
|
||||
"addToPackageJson": "dependencies"
|
||||
},
|
||||
"eas-cli": {
|
||||
"version": "3.1.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native-web": {
|
||||
"version": "~0.18.10",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native-svg": {
|
||||
"version": "13.4.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"15.6.2": {
|
||||
"version": "15.6.2-beta.0",
|
||||
"packages": {
|
||||
"expo": {
|
||||
"version": "47.0.13",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@expo/webpack-config": {
|
||||
"version": "~0.17.4",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"eas-cli": {
|
||||
"version": "3.3.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-cli": {
|
||||
"version": "6.1.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@expo/cli": {
|
||||
"version": "0.4.11",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"15.7.2": {
|
||||
"version": "15.7.2-beta.0",
|
||||
"packages": {
|
||||
"@expo/webpack-config": {
|
||||
"version": "~18.0.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"eas-cli": {
|
||||
"version": "3.5.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-cli": {
|
||||
"version": "6.3.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"15.8.3": {
|
||||
"version": "15.8.3-beta.0",
|
||||
"packages": {
|
||||
"expo": {
|
||||
"version": "48.0.4",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@expo/metro-config": {
|
||||
"version": "0.7.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-splash-screen": {
|
||||
"version": "~0.18.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-status-bar": {
|
||||
"version": "~1.4.4",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-cli": {
|
||||
"version": "6.3.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@expo/cli": {
|
||||
"version": "0.6.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"eas-cli": {
|
||||
"version": "3.7.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"babel-preset-expo": {
|
||||
"version": "~9.3.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react": {
|
||||
"version": "18.2.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-dom": {
|
||||
"version": "18.2.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-test-renderer": {
|
||||
"version": "18.2.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@types/react": {
|
||||
"version": "18.0.28",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native": {
|
||||
"version": "0.71.3",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@types/react-native": {
|
||||
"version": "0.71.3",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native-web": {
|
||||
"version": "~0.18.12",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"metro": {
|
||||
"version": "0.74.1",
|
||||
"alwaysAddToPackageJson": false,
|
||||
"addToPackageJson": "devDependencies"
|
||||
}
|
||||
}
|
||||
},
|
||||
"15.8.6": {
|
||||
"version": "15.8.6-beta.0",
|
||||
"packages": {
|
||||
"expo": {
|
||||
"version": "48.0.6",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"15.9.0": {
|
||||
"version": "15.9.0-beta.0",
|
||||
"packages": {
|
||||
"react-native": {
|
||||
"version": "0.71.4",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@types/react-native": {
|
||||
"version": "0.71.4",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"16.0.0": {
|
||||
"version": "16.0.0-beta.0",
|
||||
"packages": {
|
||||
|
||||
@ -1,45 +0,0 @@
|
||||
import { addProjectConfiguration, getProjects, Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import update from './add-new-expo-cli-targets';
|
||||
|
||||
describe('add-eas-update-target', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
addProjectConfiguration(tree, 'product', {
|
||||
root: 'apps/product',
|
||||
sourceRoot: 'apps/product/src',
|
||||
targets: {
|
||||
start: {
|
||||
executor: '@nrwl/expo:start',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it(`should update project.json with target prebuild, install and eject`, async () => {
|
||||
await update(tree);
|
||||
|
||||
getProjects(tree).forEach((project) => {
|
||||
expect(project.targets['eject']).toEqual({
|
||||
executor: 'nx:run-commands',
|
||||
options: {
|
||||
command: `nx prebuild product`,
|
||||
},
|
||||
});
|
||||
expect(project.targets['install']).toEqual({
|
||||
executor: '@nrwl/expo:install',
|
||||
options: {},
|
||||
});
|
||||
expect(project.targets['prebuild']).toEqual({
|
||||
executor: '@nrwl/expo:prebuild',
|
||||
options: {},
|
||||
});
|
||||
expect(project.targets['update']).toEqual({
|
||||
executor: '@nrwl/expo:update',
|
||||
options: {},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,68 +0,0 @@
|
||||
import {
|
||||
Tree,
|
||||
formatFiles,
|
||||
getProjects,
|
||||
updateProjectConfiguration,
|
||||
offsetFromRoot,
|
||||
} from '@nx/devkit';
|
||||
|
||||
/**
|
||||
* Add new @expo/cli targets:
|
||||
* - add target prebuild
|
||||
* - add target install
|
||||
* - add target update
|
||||
* - replace target eject
|
||||
*/
|
||||
export default async function update(tree: Tree) {
|
||||
const projects = getProjects(tree);
|
||||
|
||||
for (const [name, config] of projects.entries()) {
|
||||
if (config.targets?.['start']?.executor === '@nrwl/expo:start') {
|
||||
if (!config.targets['prebuild']) {
|
||||
config.targets['prebuild'] = {
|
||||
executor: '@nrwl/expo:prebuild',
|
||||
options: {},
|
||||
};
|
||||
}
|
||||
if (!config.targets['install']) {
|
||||
config.targets['install'] = {
|
||||
executor: '@nrwl/expo:install',
|
||||
options: {},
|
||||
};
|
||||
}
|
||||
if (!config.targets['update']) {
|
||||
config.targets['update'] = {
|
||||
executor: '@nrwl/expo:update',
|
||||
options: {},
|
||||
};
|
||||
}
|
||||
if (!config.targets['export']) {
|
||||
config.targets['export'] = {
|
||||
executor: '@nrwl/expo:export',
|
||||
options: {
|
||||
platform: 'all',
|
||||
outputDir: `${offsetFromRoot(config.root)}dist/${config.root}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
if (!config.targets['export-web']) {
|
||||
config.targets['export-web'] = {
|
||||
executor: '@nrwl/expo:export',
|
||||
options: {
|
||||
bundler: 'webpack',
|
||||
},
|
||||
};
|
||||
}
|
||||
config.targets['eject'] = {
|
||||
executor: 'nx:run-commands',
|
||||
options: {
|
||||
command: `nx prebuild ${name}`,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
updateProjectConfiguration(tree, name, config);
|
||||
}
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
@ -1,124 +0,0 @@
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
readJson,
|
||||
Tree,
|
||||
updateJson,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import update from './change-jest-preset';
|
||||
|
||||
describe('Change expo jest preset', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
updateJson(tree, 'package.json', (packageJson) => {
|
||||
packageJson.devDependencies['jest-expo'] = '*';
|
||||
return packageJson;
|
||||
});
|
||||
addProjectConfiguration(tree, 'products', {
|
||||
root: 'apps/products',
|
||||
sourceRoot: 'apps/products/src',
|
||||
targets: {
|
||||
test: {
|
||||
executor: '@nrwl/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'apps/products/jest.config.ts',
|
||||
passWithNoTests: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it(`should not remove transfrom if the code does not contain existing preprocessor`, async () => {
|
||||
tree.write(
|
||||
'apps/products/jest.config.ts',
|
||||
`module.exports = {
|
||||
preset: 'jest-expo',
|
||||
};`
|
||||
);
|
||||
await update(tree);
|
||||
|
||||
const jestConfig = tree.read('apps/products/jest.config.ts', 'utf-8');
|
||||
expect(jestConfig).toContain(`preset: 'react-native'`);
|
||||
});
|
||||
|
||||
it(`should remove transform if the code contains existing preprocessor`, async () => {
|
||||
tree.write(
|
||||
'apps/products/jest.config.ts',
|
||||
`module.exports = {
|
||||
preset: 'jest-expo',
|
||||
testRunner: 'jest-jasmine2',
|
||||
transform: {
|
||||
'\\.(js|ts|tsx)$': require.resolve('react-native/jest/preprocessor.js'),
|
||||
'^.+\\.(bmp|gif|jpg|jpeg|mp4|png|psd|svg|webp)$': require.resolve(
|
||||
'react-native/jest/assetFileTransformer.js'
|
||||
),
|
||||
}
|
||||
};`
|
||||
);
|
||||
await update(tree);
|
||||
|
||||
const jestConfig = tree.read('apps/products/jest.config.ts', 'utf-8');
|
||||
expect(jestConfig).not.toContain('transfrom');
|
||||
expect(jestConfig).not.toContain('testRunner');
|
||||
});
|
||||
|
||||
it(`should rename .babelrc to babel.config.json`, async () => {
|
||||
tree.write(
|
||||
'apps/products/jest.config.ts',
|
||||
`module.exports = {
|
||||
preset: 'jest-expo',
|
||||
};`
|
||||
);
|
||||
tree.write(
|
||||
'apps/products/.babelrc',
|
||||
`{
|
||||
"presets": ["module:metro-react-native-babel-preset"]
|
||||
}`
|
||||
);
|
||||
await update(tree);
|
||||
|
||||
expect(tree.exists('apps/products/.babelrc')).toBeFalsy();
|
||||
expect(tree.exists('apps/products/babel.config.json')).toBeTruthy();
|
||||
const babelConfigJson = tree.read(
|
||||
'apps/products/babel.config.json',
|
||||
'utf-8'
|
||||
);
|
||||
expect(babelConfigJson).toContain(
|
||||
`"presets": ["module:metro-react-native-babel-preset"]`
|
||||
);
|
||||
});
|
||||
|
||||
it(`should not rename .babelrc to babel.config.json if app is not react native`, async () => {
|
||||
tree.write(
|
||||
'apps/products/jest.config.ts',
|
||||
`module.exports = {
|
||||
preset: 'other',
|
||||
};`
|
||||
);
|
||||
tree.write(
|
||||
'apps/products/.babelrc',
|
||||
`{
|
||||
"presets": ["module:metro-react-native-babel-preset"]
|
||||
}`
|
||||
);
|
||||
await update(tree);
|
||||
|
||||
expect(tree.exists('apps/products/.babelrc')).toBeTruthy();
|
||||
expect(tree.exists('apps/products/babel.config.json')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should remove jest-expo from package.json', async () => {
|
||||
tree.write(
|
||||
'apps/products/jest.config.ts',
|
||||
`module.exports = {
|
||||
preset: 'jest-expo',
|
||||
};`
|
||||
);
|
||||
await update(tree);
|
||||
const packageJson = readJson(tree, 'package.json');
|
||||
expect(packageJson.devDependencies['jest-expo']).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@ -1,90 +0,0 @@
|
||||
import {
|
||||
formatFiles,
|
||||
logger,
|
||||
ProjectConfiguration,
|
||||
readProjectConfiguration,
|
||||
stripIndents,
|
||||
Tree,
|
||||
updateJson,
|
||||
} from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import {
|
||||
addPropertyToJestConfig,
|
||||
removePropertyFromJestConfig,
|
||||
} from '@nx/jest';
|
||||
import { JestExecutorOptions } from '@nx/jest/src/executors/jest/schema';
|
||||
import { join } from 'path';
|
||||
|
||||
/**
|
||||
* Change the preset in expo's jest config
|
||||
* - remove transform and testRunner from jest config
|
||||
* - changes preset from jest-expo to react-native
|
||||
* - change to babel.config.json
|
||||
*/
|
||||
export default async function update(tree: Tree) {
|
||||
let useJestExpo = false;
|
||||
forEachExecutorOptions<JestExecutorOptions>(
|
||||
tree,
|
||||
'@nrwl/jest:jest',
|
||||
(options, projectName) => {
|
||||
if (options.jestConfig && tree.exists(options.jestConfig)) {
|
||||
const jestConfig = tree.read(options.jestConfig, 'utf-8');
|
||||
|
||||
if (jestConfig.includes(`preset: 'jest-expo'`)) {
|
||||
const project = readProjectConfiguration(tree, projectName);
|
||||
changePreset(tree, project);
|
||||
renameBabelJson(tree, project);
|
||||
useJestExpo = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (useJestExpo) {
|
||||
removeJestExpoFromPackageJson(tree);
|
||||
}
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
function changePreset(host: Tree, project: ProjectConfiguration) {
|
||||
const jestConfigPath = project.targets?.test?.options?.jestConfig;
|
||||
if (!jestConfigPath || !host.exists(jestConfigPath)) return;
|
||||
try {
|
||||
removePropertyFromJestConfig(host, jestConfigPath, 'transform');
|
||||
removePropertyFromJestConfig(host, jestConfigPath, 'testRunner');
|
||||
removePropertyFromJestConfig(host, jestConfigPath, 'preset');
|
||||
addPropertyToJestConfig(host, jestConfigPath, 'preset', 'react-native');
|
||||
} catch {
|
||||
logger.error(
|
||||
stripIndents`Unable to update ${jestConfigPath} for project ${project.root}.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function renameBabelJson(host: Tree, project: ProjectConfiguration) {
|
||||
const babelrcPath = join(project.root, '.babelrc');
|
||||
const babelJsonPath = join(project.root, 'babel.config.json');
|
||||
if (!host.exists(babelrcPath)) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const buffer = host.read(babelrcPath);
|
||||
if (!buffer) {
|
||||
return;
|
||||
}
|
||||
host.write(babelJsonPath, buffer);
|
||||
host.delete(babelrcPath);
|
||||
} catch {
|
||||
logger.error(
|
||||
stripIndents`Unable to rename from ${babelrcPath} to ${babelJsonPath} for project ${project.root}.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function removeJestExpoFromPackageJson(tree: Tree) {
|
||||
updateJson(tree, 'package.json', (packageJson) => {
|
||||
delete packageJson.devDependencies['jest-expo'];
|
||||
return packageJson;
|
||||
});
|
||||
}
|
||||
@ -1,56 +0,0 @@
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
getProjects,
|
||||
readJson,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import update from './change-webpack-to-metro';
|
||||
|
||||
describe('change-webpack-to-metro', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
addProjectConfiguration(tree, 'product', {
|
||||
root: 'apps/product',
|
||||
sourceRoot: 'apps/product/src',
|
||||
targets: {
|
||||
start: {
|
||||
executor: '@nrwl/expo:start',
|
||||
},
|
||||
'export-web': {
|
||||
executor: '@nrwl/expo:export',
|
||||
options: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
tree.write(
|
||||
`apps/product/app.json`,
|
||||
JSON.stringify({
|
||||
expo: {
|
||||
web: {
|
||||
bundler: 'webpack',
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it(`should update project.json with target export-web and change app.json`, async () => {
|
||||
await update(tree);
|
||||
|
||||
getProjects(tree).forEach((project) => {
|
||||
expect(project.targets['export-web']).toEqual({
|
||||
executor: '@nrwl/expo:export',
|
||||
options: {
|
||||
bundler: 'metro',
|
||||
},
|
||||
});
|
||||
|
||||
expect(project.targets['web']).toBeUndefined();
|
||||
const appJson = readJson(tree, `apps/product/app.json`);
|
||||
expect(appJson.expo.web.bundler).toEqual('metro');
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,39 +0,0 @@
|
||||
import {
|
||||
Tree,
|
||||
formatFiles,
|
||||
getProjects,
|
||||
updateProjectConfiguration,
|
||||
updateJson,
|
||||
} from '@nx/devkit';
|
||||
|
||||
/**
|
||||
* Change webpack to metro
|
||||
* - change target export-web
|
||||
* - delete webpack.config.js
|
||||
* - delete @expo/webpack-config dependency
|
||||
*/
|
||||
export default async function update(tree: Tree) {
|
||||
const projects = getProjects(tree);
|
||||
|
||||
for (const [name, config] of projects.entries()) {
|
||||
if (config.targets?.['start']?.executor === '@nrwl/expo:start') {
|
||||
if (config.targets['web']) {
|
||||
delete config.targets['web'];
|
||||
}
|
||||
if (config.targets['export-web']) {
|
||||
config.targets['export-web'].options.bundler = 'metro';
|
||||
}
|
||||
|
||||
updateJson(tree, `${config.root}/app.json`, (appJson) => {
|
||||
if (appJson.expo?.web) {
|
||||
appJson.expo.web.bundler = 'metro';
|
||||
}
|
||||
return appJson;
|
||||
});
|
||||
}
|
||||
|
||||
updateProjectConfiguration(tree, name, config);
|
||||
}
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
@ -1,23 +1,5 @@
|
||||
{
|
||||
"generators": {
|
||||
"add-jest-inputs": {
|
||||
"version": "15.0.0-beta.0",
|
||||
"cli": "nx",
|
||||
"description": "Stop hashing jest spec files and config files for build targets and dependent tasks",
|
||||
"factory": "./src/migrations/update-15-0-0/add-jest-inputs"
|
||||
},
|
||||
"update-configs-jest-29": {
|
||||
"version": "15.8.0-beta.0",
|
||||
"cli": "nx",
|
||||
"description": "Update jest configs to support jest 29 changes (https://jestjs.io/docs/upgrading-to-jest29)",
|
||||
"factory": "./src/migrations/update-15-8-0/update-configs-jest-29"
|
||||
},
|
||||
"update-tests-jest-29": {
|
||||
"version": "15.8.0-beta.0",
|
||||
"cli": "nx",
|
||||
"description": "Update jest test files to support jest 29 changes (https://jestjs.io/docs/upgrading-to-jest29)",
|
||||
"factory": "./src/migrations/update-15-8-0/update-tests-jest-29"
|
||||
},
|
||||
"update-16-0-0-add-nx-packages": {
|
||||
"cli": "nx",
|
||||
"version": "16.0.0-beta.1",
|
||||
@ -37,52 +19,6 @@
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
"15.0.1-beta.3": {
|
||||
"version": "15.0.1-beta.3",
|
||||
"packages": {
|
||||
"jest-environment-jsdom": {
|
||||
"version": "~28.1.1",
|
||||
"addToPackageJson": "devDependencies"
|
||||
}
|
||||
}
|
||||
},
|
||||
"15.8.0": {
|
||||
"version": "15.8.0-beta.0",
|
||||
"packages": {
|
||||
"jest": {
|
||||
"version": "~29.4.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@types/jest": {
|
||||
"version": "~29.4.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expect": {
|
||||
"version": "~29.4.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@jest/globals": {
|
||||
"version": "~29.4.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"jest-jasmine2": {
|
||||
"version": "~29.4.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"jest-environment-jsdom": {
|
||||
"version": "~29.4.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"ts-jest": {
|
||||
"version": "~29.0.5",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"babel-jest": {
|
||||
"version": "~29.4.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"16.0.0": {
|
||||
"version": "16.0.0-beta.4",
|
||||
"packages": {
|
||||
|
||||
@ -1,80 +0,0 @@
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
readNxJson,
|
||||
Tree,
|
||||
updateNxJson,
|
||||
} from '@nx/devkit';
|
||||
import addJestInputs from './add-jest-inputs';
|
||||
|
||||
describe('15.0.0 migration (add-jest-inputs)', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
});
|
||||
|
||||
it('should add inputs configuration for jest targets', async () => {
|
||||
updateNxJson(tree, {
|
||||
namedInputs: {
|
||||
default: ['{projectRoot}/**/*', 'sharedGlobals'],
|
||||
sharedGlobals: [],
|
||||
production: ['default'],
|
||||
},
|
||||
});
|
||||
addProjectConfiguration(tree, 'proj', {
|
||||
root: 'proj',
|
||||
targets: {
|
||||
test: {
|
||||
executor: '@nrwl/jest:jest',
|
||||
options: {},
|
||||
},
|
||||
test2: {
|
||||
executor: '@nrwl/jest:jest',
|
||||
options: {},
|
||||
},
|
||||
notTest: {
|
||||
executor: 'nx:run-commands',
|
||||
},
|
||||
},
|
||||
});
|
||||
tree.write('jest.preset.js', '');
|
||||
|
||||
await addJestInputs(tree);
|
||||
|
||||
const updated = readNxJson(tree);
|
||||
expect(updated).toMatchInlineSnapshot(`
|
||||
{
|
||||
"namedInputs": {
|
||||
"default": [
|
||||
"{projectRoot}/**/*",
|
||||
"sharedGlobals",
|
||||
],
|
||||
"production": [
|
||||
"default",
|
||||
"!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)",
|
||||
"!{projectRoot}/tsconfig.spec.json",
|
||||
"!{projectRoot}/jest.config.[jt]s",
|
||||
],
|
||||
"sharedGlobals": [],
|
||||
},
|
||||
"targetDefaults": {
|
||||
"test": {
|
||||
"inputs": [
|
||||
"default",
|
||||
"^production",
|
||||
"{workspaceRoot}/jest.preset.js",
|
||||
],
|
||||
},
|
||||
"test2": {
|
||||
"inputs": [
|
||||
"default",
|
||||
"^production",
|
||||
"{workspaceRoot}/jest.preset.js",
|
||||
],
|
||||
},
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
@ -1,46 +0,0 @@
|
||||
import { formatFiles, readNxJson, Tree, updateNxJson } from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
|
||||
export default async function (tree: Tree) {
|
||||
const nxJson = readNxJson(tree);
|
||||
|
||||
const jestTargets = getJestTargetNames(tree);
|
||||
const hasProductionFileset = !!nxJson.namedInputs?.production;
|
||||
|
||||
if (jestTargets.size > 0 && hasProductionFileset) {
|
||||
const productionFileset = new Set(nxJson.namedInputs.production);
|
||||
for (const exclusion of [
|
||||
'!{projectRoot}/**/?(*.)+(spec|test).[jt]s?(x)?(.snap)',
|
||||
'!{projectRoot}/tsconfig.spec.json',
|
||||
'!{projectRoot}/jest.config.[jt]s',
|
||||
]) {
|
||||
productionFileset.add(exclusion);
|
||||
}
|
||||
nxJson.namedInputs.production = Array.from(productionFileset);
|
||||
}
|
||||
|
||||
for (const targetName of jestTargets) {
|
||||
nxJson.targetDefaults ??= {};
|
||||
const jestTargetDefaults = (nxJson.targetDefaults[targetName] ??= {});
|
||||
|
||||
jestTargetDefaults.inputs ??= [
|
||||
'default',
|
||||
hasProductionFileset ? '^production' : '^default',
|
||||
...(tree.exists('jest.preset.js')
|
||||
? ['{workspaceRoot}/jest.preset.js']
|
||||
: []),
|
||||
];
|
||||
}
|
||||
|
||||
updateNxJson(tree, nxJson);
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
function getJestTargetNames(tree: Tree) {
|
||||
const jestTargetNames = new Set<string>();
|
||||
forEachExecutorOptions(tree, '@nrwl/jest:jest', (_, __, target) => {
|
||||
jestTargetNames.add(target);
|
||||
});
|
||||
return jestTargetNames;
|
||||
}
|
||||
@ -1,493 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Jest Migration - jest 29 update configs should NOT update ts-jest with no globals are preset 1`] = `
|
||||
"const nxPreset = require('@nrwl/jest/preset').default;
|
||||
module.exports = {
|
||||
...nxPreset,
|
||||
testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'],
|
||||
transform: {
|
||||
'^.+.(ts|js|html)$': 'ts-jest',
|
||||
},
|
||||
resolver: '@nrwl/jest/plugins/resolver',
|
||||
moduleFileExtensions: ['ts', 'js', 'html'],
|
||||
coverageReporters: ['html'],
|
||||
/* TODO: Update to latest Jest snapshotFormat
|
||||
* By default Nx has kept the older style of Jest Snapshot formats
|
||||
* to prevent breaking of any existing tests with snapshots.
|
||||
* It's recommend you update to the latest format.
|
||||
* You can do this by removing snapshotFormat property
|
||||
* and running tests with --update-snapshot flag.
|
||||
* Example: "nx affected --targets=test --update-snapshot"
|
||||
* More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format
|
||||
*/
|
||||
snapshotFormat: { escapeString: true, printBasicPrototype: true },
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 update configs should add snapshot config with no root preset 1`] = `
|
||||
"/* eslint-disable */
|
||||
export default {
|
||||
displayName: 'my-lib',
|
||||
preset: '../../jest.preset.js',
|
||||
globals: {},
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': [
|
||||
'ts-jest',
|
||||
{
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
},
|
||||
],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||
coverageDirectory: '../../coverage/libs/my-lib',
|
||||
/* TODO: Update to latest Jest snapshotFormat
|
||||
* By default Nx has kept the older style of Jest Snapshot formats
|
||||
* to prevent breaking of any existing tests with snapshots.
|
||||
* It's recommend you update to the latest format.
|
||||
* You can do this by removing snapshotFormat property
|
||||
* and running tests with --update-snapshot flag.
|
||||
* Example: From within the project directory, run "nx test --update-snapshot"
|
||||
* More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format
|
||||
*/
|
||||
snapshotFormat: { escapeString: true, printBasicPrototype: true },
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 update configs should add snapshot config with no root preset 2`] = `
|
||||
"module.exports = {
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': [
|
||||
'ts-jest',
|
||||
{
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
},
|
||||
],
|
||||
},
|
||||
// I am a comment and shouldn't be removed
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
|
||||
globals: { something: 'else', abc: [1234, true, { abc: 'yes' }] },
|
||||
/**
|
||||
* Multi-line comment shouldn't be removed
|
||||
*/
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js',
|
||||
/* TODO: Update to latest Jest snapshotFormat
|
||||
* By default Nx has kept the older style of Jest Snapshot formats
|
||||
* to prevent breaking of any existing tests with snapshots.
|
||||
* It's recommend you update to the latest format.
|
||||
* You can do this by removing snapshotFormat property
|
||||
* and running tests with --update-snapshot flag.
|
||||
* Example: From within the project directory, run "nx test --update-snapshot"
|
||||
* More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format
|
||||
*/
|
||||
snapshotFormat: { escapeString: true, printBasicPrototype: true },
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 update configs should be idempotent 1`] = `
|
||||
"/* eslint-disable */
|
||||
export default {
|
||||
displayName: 'my-lib',
|
||||
preset: '../../jest.preset.js',
|
||||
globals: {},
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': [
|
||||
'ts-jest',
|
||||
{
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
},
|
||||
],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||
coverageDirectory: '../../coverage/libs/my-lib',
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 update configs should be idempotent 2`] = `
|
||||
"module.exports = {
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': [
|
||||
'ts-jest',
|
||||
{
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
},
|
||||
],
|
||||
},
|
||||
// I am a comment and shouldn't be removed
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
|
||||
globals: { something: 'else', abc: [1234, true, { abc: 'yes' }] },
|
||||
/**
|
||||
* Multi-line comment shouldn't be removed
|
||||
*/
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js',
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 update configs should update globalThis.ngJest.teardown to testEnvironmentOptions 1`] = `
|
||||
"globalThis.ngJest = {};
|
||||
|
||||
export default {
|
||||
globals: {},
|
||||
transform: {
|
||||
'^.+.(ts|mjs|js|html)$': [
|
||||
'jest-preset-angular',
|
||||
{
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
stringifyContentPathRegex: '.(html|svg)$',
|
||||
},
|
||||
],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js',
|
||||
testEnvironmentOptions: { teardown: true },
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 update configs should update globalThis.ngJest.teardown to testEnvironmentOptions 2`] = `
|
||||
"globalThis.ngJest = {
|
||||
ngcc: true,
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
globals: {},
|
||||
transform: {
|
||||
'^.+.(ts|mjs|js|html)$': [
|
||||
'jest-preset-angular',
|
||||
{
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
stringifyContentPathRegex: '.(html|svg)$',
|
||||
},
|
||||
],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
|
||||
testEnvironmentOptions: {
|
||||
blah: 123,
|
||||
teardown: false,
|
||||
},
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js',
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 update configs should update jest.config.ts 1`] = `
|
||||
"/* eslint-disable */
|
||||
export default {
|
||||
displayName: 'my-lib',
|
||||
preset: '../../jest.preset.js',
|
||||
globals: {},
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': [
|
||||
'ts-jest',
|
||||
{
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
},
|
||||
],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||
coverageDirectory: '../../coverage/libs/my-lib',
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 update configs should update jest.config.ts 2`] = `
|
||||
"module.exports = {
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': [
|
||||
'ts-jest',
|
||||
{
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
},
|
||||
],
|
||||
},
|
||||
// I am a comment and shouldn't be removed
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
|
||||
globals: { something: 'else', abc: [1234, true, { abc: 'yes' }] },
|
||||
/**
|
||||
* Multi-line comment shouldn't be removed
|
||||
*/
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js',
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 update configs should update root preset 1`] = `
|
||||
"const nxPreset = require('@nrwl/jest/preset').default;
|
||||
|
||||
module.exports = {
|
||||
...nxPreset,
|
||||
/* TODO: Update to latest Jest snapshotFormat
|
||||
* By default Nx has kept the older style of Jest Snapshot formats
|
||||
* to prevent breaking of any existing tests with snapshots.
|
||||
* It's recommend you update to the latest format.
|
||||
* You can do this by removing snapshotFormat property
|
||||
* and running tests with --update-snapshot flag.
|
||||
* Example: "nx affected --targets=test --update-snapshot"
|
||||
* More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format
|
||||
*/
|
||||
snapshotFormat: { escapeString: true, printBasicPrototype: true },
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 update configs should update root preset 2`] = `
|
||||
"/* eslint-disable */
|
||||
export default {
|
||||
displayName: 'my-lib',
|
||||
preset: '../../jest.preset.js',
|
||||
globals: {},
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': [
|
||||
'ts-jest',
|
||||
{
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
},
|
||||
],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||
coverageDirectory: '../../coverage/libs/my-lib',
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 update configs should update root preset 3`] = `
|
||||
"module.exports = {
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': [
|
||||
'ts-jest',
|
||||
{
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
},
|
||||
],
|
||||
},
|
||||
// I am a comment and shouldn't be removed
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
|
||||
globals: { something: 'else', abc: [1234, true, { abc: 'yes' }] },
|
||||
/**
|
||||
* Multi-line comment shouldn't be removed
|
||||
*/
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js',
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 update configs should update root preset if ts-jest is preset 1`] = `
|
||||
"const nxPreset = require('@nrwl/jest/preset').default;
|
||||
module.exports = {
|
||||
...nxPreset,
|
||||
testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'],
|
||||
globals: { something: 'else', abc: [1234, true, { abc: 'yes' }] },
|
||||
transform: {
|
||||
'^.+.(ts|js|html)$': [
|
||||
'ts-jest',
|
||||
{
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
},
|
||||
],
|
||||
},
|
||||
resolver: '@nrwl/jest/plugins/resolver',
|
||||
moduleFileExtensions: ['ts', 'js', 'html'],
|
||||
coverageReporters: ['html'],
|
||||
/* TODO: Update to latest Jest snapshotFormat
|
||||
* By default Nx has kept the older style of Jest Snapshot formats
|
||||
* to prevent breaking of any existing tests with snapshots.
|
||||
* It's recommend you update to the latest format.
|
||||
* You can do this by removing snapshotFormat property
|
||||
* and running tests with --update-snapshot flag.
|
||||
* Example: "nx affected --targets=test --update-snapshot"
|
||||
* More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format
|
||||
*/
|
||||
snapshotFormat: { escapeString: true, printBasicPrototype: true },
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 update configs should work if not using ts-jest transformer 1`] = `
|
||||
"export default {
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': 'babel-jest',
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js',
|
||||
};"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 update configs should work if not using ts-jest transformer 2`] = `
|
||||
"module.exports = {
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': 'babel-jest',
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js',
|
||||
};"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 update configs should work snapshotFormat is defined 1`] = `
|
||||
"export default {
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': 'babel-jest',
|
||||
},
|
||||
globals: { something: 'else',
|
||||
abc: [1234, true, {abc: 'yes'}] },
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js',
|
||||
snapshotFormat: {escapeString: false, printBasicPrototype: true}
|
||||
};"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 update configs should work snapshotFormat is defined 2`] = `
|
||||
"module.exports = {
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': ['ts-jest', {
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json'
|
||||
}],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js',
|
||||
globals: { something: 'else',
|
||||
abc: [1234, true, {abc: 'yes'}] },
|
||||
snapshotFormat: {escapeString: false, printBasicPrototype: true}
|
||||
};"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 update configs should work with jest-preset-angular 1`] = `
|
||||
"export default {
|
||||
globals: { },
|
||||
transform: {
|
||||
'^.+.(ts|mjs|js|html)$': ['jest-preset-angular', {
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
stringifyContentPathRegex: '\\.(html|svg)$',
|
||||
}],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js',
|
||||
};"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 update configs should work with jest-preset-angular 2`] = `
|
||||
"module.exports = {
|
||||
globals: { },
|
||||
transform: {
|
||||
'^.+.(ts|mjs|js|html)$': ['jest-preset-angular', {
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
stringifyContentPathRegex: '\\.(html|svg)$',
|
||||
}],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js',
|
||||
};"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 update configs should work with multiple projects + configs 1`] = `
|
||||
"/* eslint-disable */
|
||||
export default {
|
||||
displayName: 'my-lib',
|
||||
preset: '../../jest.preset.js',
|
||||
globals: {},
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': [
|
||||
'ts-jest',
|
||||
{
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
},
|
||||
],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||
coverageDirectory: '../../coverage/libs/my-lib',
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 update configs should work with multiple projects + configs 2`] = `
|
||||
"module.exports = {
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': [
|
||||
'ts-jest',
|
||||
{
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
},
|
||||
],
|
||||
},
|
||||
// I am a comment and shouldn't be removed
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
|
||||
globals: { something: 'else', abc: [1234, true, { abc: 'yes' }] },
|
||||
/**
|
||||
* Multi-line comment shouldn't be removed
|
||||
*/
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js',
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 update configs should work with multiple projects + configs 3`] = `
|
||||
"/* eslint-disable */
|
||||
export default {
|
||||
displayName: 'another-lib',
|
||||
preset: '../../jest.preset.js',
|
||||
globals: {},
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': [
|
||||
'ts-jest',
|
||||
{
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
},
|
||||
],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||
coverageDirectory: '../../coverage/libs/another-lib',
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 update configs should work with multiple projects + configs 4`] = `
|
||||
"module.exports = {
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': [
|
||||
'ts-jest',
|
||||
{
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
},
|
||||
],
|
||||
},
|
||||
// I am a comment and shouldn't be removed
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
|
||||
globals: { something: 'else', abc: [1234, true, { abc: 'yes' }] },
|
||||
/**
|
||||
* Multi-line comment shouldn't be removed
|
||||
*/
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js',
|
||||
};
|
||||
"
|
||||
`;
|
||||
@ -1,199 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Jest Migration - jest 29 mocked usage in tests should be idempotent 1`] = `
|
||||
"import { Mocked, MockedShallow } from 'jest-mock';
|
||||
import { expect, jest, test } from '@jest/globals';
|
||||
import { song } from './song';
|
||||
|
||||
jest.mock('./song');
|
||||
jest.spyOn(console, 'log');
|
||||
|
||||
const mockedSong = jest.mocked(song);
|
||||
// or through \`jest.Mocked<Source>\`
|
||||
// const mockedSong = song as jest.Mocked<typeof song>;
|
||||
|
||||
test('deep method is typed correctly', () => {
|
||||
mockedSong.one.more.time.mockReturnValue(12);
|
||||
|
||||
expect(mockedSong.one.more.time(10)).toBe(12);
|
||||
expect(mockedSong.one.more.time.mock.calls).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('direct usage', () => {
|
||||
jest.mocked(console.log).mockImplementation(() => {
|
||||
return;
|
||||
});
|
||||
|
||||
console.log('one more time');
|
||||
|
||||
expect(jest.mocked(console.log, { shallow: true }).mock.calls).toHaveLength(
|
||||
1
|
||||
);
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 mocked usage in tests should be idempotent 2`] = `
|
||||
"const { Mocked, MockedShallow } = require('jest-mock');
|
||||
const { expect, jest, test } = require('@jest/globals');
|
||||
const { song } = require('./song');
|
||||
|
||||
jest.mock('./song');
|
||||
jest.spyOn(console, 'log');
|
||||
|
||||
const mockedSong = jest.mocked(song);
|
||||
// or through \`jest.Mocked<Source>\`
|
||||
// const mockedSong = song as jest.Mocked<typeof song>;
|
||||
|
||||
test('deep method is typed correctly', () => {
|
||||
mockedSong.one.more.time.mockReturnValue(12);
|
||||
|
||||
expect(mockedSong.one.more.time(10)).toBe(12);
|
||||
expect(mockedSong.one.more.time.mock.calls).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('direct usage', () => {
|
||||
jest.mocked(console.log).mockImplementation(() => {
|
||||
return;
|
||||
});
|
||||
|
||||
console.log('one more time');
|
||||
|
||||
expect(jest.mocked(console.log, { shallow: true }).mock.calls).toHaveLength(
|
||||
1
|
||||
);
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 mocked usage in tests should be idempotent 3`] = `
|
||||
"import { Mocked, MockedShallow } from 'jest-mock';
|
||||
import { expect, jest, test } from '@jest/globals';
|
||||
import { song } from './song';
|
||||
|
||||
jest.mock('./song');
|
||||
jest.spyOn(console, 'log');
|
||||
|
||||
const mockedSong = jest.mocked(song);
|
||||
// or through \`jest.Mocked<Source>\`
|
||||
// const mockedSong = song as jest.Mocked<typeof song>;
|
||||
|
||||
test('deep method is typed correctly', () => {
|
||||
mockedSong.one.more.time.mockReturnValue(12);
|
||||
|
||||
expect(mockedSong.one.more.time(10)).toBe(12);
|
||||
expect(mockedSong.one.more.time.mock.calls).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('direct usage', () => {
|
||||
jest.mocked(console.log).mockImplementation(() => {
|
||||
return;
|
||||
});
|
||||
|
||||
console.log('one more time');
|
||||
|
||||
expect(jest.mocked(console.log, { shallow: true }).mock.calls).toHaveLength(
|
||||
1
|
||||
);
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 mocked usage in tests should be idempotent 4`] = `
|
||||
"const { Mocked, MockedShallow } = require('jest-mock');
|
||||
const { expect, jest, test } = require('@jest/globals');
|
||||
const { song } = require('./song');
|
||||
|
||||
jest.mock('./song');
|
||||
jest.spyOn(console, 'log');
|
||||
|
||||
const mockedSong = jest.mocked(song);
|
||||
// or through \`jest.Mocked<Source>\`
|
||||
// const mockedSong = song as jest.Mocked<typeof song>;
|
||||
|
||||
test('deep method is typed correctly', () => {
|
||||
mockedSong.one.more.time.mockReturnValue(12);
|
||||
|
||||
expect(mockedSong.one.more.time(10)).toBe(12);
|
||||
expect(mockedSong.one.more.time.mock.calls).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('direct usage', () => {
|
||||
jest.mocked(console.log).mockImplementation(() => {
|
||||
return;
|
||||
});
|
||||
|
||||
console.log('one more time');
|
||||
|
||||
expect(jest.mocked(console.log, { shallow: true }).mock.calls).toHaveLength(
|
||||
1
|
||||
);
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 mocked usage in tests should not update anything if there are no tests 1`] = `
|
||||
"import { Mocked, MockedShallow } from 'jest-mock';
|
||||
import { expect, jest, test } from '@jest/globals';
|
||||
import { song } from './song';
|
||||
|
||||
jest.mock('./song');
|
||||
jest.spyOn(console, 'log');
|
||||
|
||||
const mockedSong = jest.mocked(song);
|
||||
// or through \`jest.Mocked<Source>\`
|
||||
// const mockedSong = song as jest.Mocked<typeof song>;
|
||||
|
||||
test('deep method is typed correctly', () => {
|
||||
mockedSong.one.more.time.mockReturnValue(12);
|
||||
|
||||
expect(mockedSong.one.more.time(10)).toBe(12);
|
||||
expect(mockedSong.one.more.time.mock.calls).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('direct usage', () => {
|
||||
jest.mocked(console.log).mockImplementation(() => {
|
||||
return;
|
||||
});
|
||||
|
||||
console.log('one more time');
|
||||
|
||||
expect(jest.mocked(console.log, { shallow: true }).mock.calls).toHaveLength(
|
||||
1
|
||||
);
|
||||
});
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration - jest 29 mocked usage in tests should not update anything if there are no tests 2`] = `
|
||||
"const { Mocked, MockedShallow } = require('jest-mock');
|
||||
const { expect, jest, test } = require('@jest/globals');
|
||||
const { song } = require('./song');
|
||||
|
||||
jest.mock('./song');
|
||||
jest.spyOn(console, 'log');
|
||||
|
||||
const mockedSong = jest.mocked(song);
|
||||
// or through \`jest.Mocked<Source>\`
|
||||
// const mockedSong = song as jest.Mocked<typeof song>;
|
||||
|
||||
test('deep method is typed correctly', () => {
|
||||
mockedSong.one.more.time.mockReturnValue(12);
|
||||
|
||||
expect(mockedSong.one.more.time(10)).toBe(12);
|
||||
expect(mockedSong.one.more.time.mock.calls).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('direct usage', () => {
|
||||
jest.mocked(console.log).mockImplementation(() => {
|
||||
return;
|
||||
});
|
||||
|
||||
console.log('one more time');
|
||||
|
||||
expect(jest.mocked(console.log, { shallow: true }).mock.calls).toHaveLength(
|
||||
1
|
||||
);
|
||||
});
|
||||
"
|
||||
`;
|
||||
@ -1,490 +0,0 @@
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import {
|
||||
ProjectGraph,
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { updateConfigsJest29 } from './update-configs-jest-29';
|
||||
import { libraryGenerator } from '@nx/js';
|
||||
|
||||
let projectGraph: ProjectGraph;
|
||||
jest.mock('@nx/devkit', () => ({
|
||||
...jest.requireActual<any>('@nx/devkit'),
|
||||
createProjectGraphAsync: jest.fn().mockImplementation(async () => {
|
||||
return projectGraph;
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('Jest Migration - jest 29 update configs', () => {
|
||||
let tree: Tree;
|
||||
|
||||
let originalEnv: string;
|
||||
beforeEach(() => {
|
||||
originalEnv = process.env.NX_ADD_PLUGINS;
|
||||
process.env.NX_ADD_PLUGINS = 'false';
|
||||
});
|
||||
afterEach(() => {
|
||||
process.env.NX_ADD_PLUGINS = originalEnv;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
});
|
||||
afterAll(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
it('should update jest.config.ts', async () => {
|
||||
await setup(tree, 'my-lib');
|
||||
|
||||
await updateConfigsJest29(tree);
|
||||
|
||||
const actualJestConfigTs = tree.read('libs/my-lib/jest.config.ts', 'utf-8');
|
||||
expect(actualJestConfigTs).toMatchSnapshot();
|
||||
const actualJestConfigJs = tree.read('libs/my-lib/jest.config.js', 'utf-8');
|
||||
expect(actualJestConfigJs).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should update root preset', async () => {
|
||||
await setup(tree, 'my-lib');
|
||||
await updateConfigsJest29(tree);
|
||||
|
||||
const actualPreset = tree.read('jest.preset.js', 'utf-8');
|
||||
expect(actualPreset).toMatchSnapshot();
|
||||
const actualJestConfigTs = tree.read('libs/my-lib/jest.config.ts', 'utf-8');
|
||||
expect(actualJestConfigTs).toMatchSnapshot();
|
||||
const actualJestConfigJs = tree.read('libs/my-lib/jest.config.js', 'utf-8');
|
||||
expect(actualJestConfigJs).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should update root preset if ts-jest is preset', async () => {
|
||||
await setup(tree, 'my-lib');
|
||||
tree.write(
|
||||
'jest.preset.js',
|
||||
`const nxPreset = require('@nrwl/jest/preset').default;
|
||||
module.exports = {
|
||||
...nxPreset,
|
||||
testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json'
|
||||
},
|
||||
something: 'else',
|
||||
abc: [1234, true, {abc: 'yes'}]
|
||||
},
|
||||
transform: {
|
||||
'^.+\\.(ts|js|html)$': 'ts-jest',
|
||||
},
|
||||
resolver: '@nrwl/jest/plugins/resolver',
|
||||
moduleFileExtensions: ['ts', 'js', 'html'],
|
||||
coverageReporters: ['html'],
|
||||
};
|
||||
`
|
||||
);
|
||||
|
||||
await updateConfigsJest29(tree);
|
||||
|
||||
const actualPreset = tree.read('jest.preset.js', 'utf-8');
|
||||
expect(actualPreset).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should NOT update ts-jest with no globals are preset', async () => {
|
||||
await setup(tree, 'my-lib');
|
||||
tree.write(
|
||||
'jest.preset.js',
|
||||
`const nxPreset = require('@nrwl/jest/preset').default;
|
||||
module.exports = {
|
||||
...nxPreset,
|
||||
testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'],
|
||||
transform: {
|
||||
'^.+\\.(ts|js|html)$': 'ts-jest',
|
||||
},
|
||||
resolver: '@nrwl/jest/plugins/resolver',
|
||||
moduleFileExtensions: ['ts', 'js', 'html'],
|
||||
coverageReporters: ['html'],
|
||||
};
|
||||
`
|
||||
);
|
||||
|
||||
await updateConfigsJest29(tree);
|
||||
|
||||
const actualPreset = tree.read('jest.preset.js', 'utf-8');
|
||||
expect(actualPreset).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should add snapshot config with no root preset', async () => {
|
||||
await setup(tree, 'my-lib');
|
||||
|
||||
tree.delete('jest.preset.js');
|
||||
|
||||
await updateConfigsJest29(tree);
|
||||
|
||||
const actualJestConfigTs = tree.read('libs/my-lib/jest.config.ts', 'utf-8');
|
||||
expect(actualJestConfigTs).toMatchSnapshot();
|
||||
const actualJestConfigJs = tree.read('libs/my-lib/jest.config.js', 'utf-8');
|
||||
expect(actualJestConfigJs).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should work with multiple projects + configs', async () => {
|
||||
await setup(tree, 'my-lib');
|
||||
await setup(tree, 'another-lib', projectGraph);
|
||||
await updateConfigsJest29(tree);
|
||||
|
||||
const actualJestConfigTs1 = tree.read(
|
||||
'libs/my-lib/jest.config.ts',
|
||||
'utf-8'
|
||||
);
|
||||
expect(actualJestConfigTs1).toMatchSnapshot();
|
||||
const actualJestConfigJs1 = tree.read(
|
||||
'libs/my-lib/jest.config.js',
|
||||
'utf-8'
|
||||
);
|
||||
expect(actualJestConfigJs1).toMatchSnapshot();
|
||||
|
||||
const actualJestConfigTs2 = tree.read(
|
||||
'libs/another-lib/jest.config.ts',
|
||||
'utf-8'
|
||||
);
|
||||
expect(actualJestConfigTs2).toMatchSnapshot();
|
||||
const actualJestConfigJs2 = tree.read(
|
||||
'libs/another-lib/jest.config.js',
|
||||
'utf-8'
|
||||
);
|
||||
expect(actualJestConfigJs2).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should update globalThis.ngJest.teardown to testEnvironmentOptions ', async () => {
|
||||
await setup(tree, 'jest-preset-angular');
|
||||
tree.write(
|
||||
`libs/jest-preset-angular/jest.config.ts`,
|
||||
`globalThis.ngJest = {
|
||||
teardown: true
|
||||
}
|
||||
|
||||
export default {
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
stringifyContentPathRegex: '\\.(html|svg)$',
|
||||
}
|
||||
},
|
||||
transform: {
|
||||
'^.+.(ts|mjs|js|html)$': 'jest-preset-angular',
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js',
|
||||
};`
|
||||
);
|
||||
tree.write(
|
||||
`libs/jest-preset-angular/jest.config.js`,
|
||||
`
|
||||
globalThis.ngJest = {
|
||||
ngcc: true,
|
||||
teardown: false
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
stringifyContentPathRegex: '\\.(html|svg)$',
|
||||
}
|
||||
},
|
||||
transform: {
|
||||
'^.+.(ts|mjs|js|html)$': 'jest-preset-angular',
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
|
||||
testEnvironmentOptions: {
|
||||
blah: 123,
|
||||
},
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js',
|
||||
};`
|
||||
);
|
||||
await updateConfigsJest29(tree);
|
||||
const jpaJestConfigTs = tree.read(
|
||||
`libs/jest-preset-angular/jest.config.ts`,
|
||||
'utf-8'
|
||||
);
|
||||
expect(jpaJestConfigTs).toMatchSnapshot();
|
||||
const jpaJestConfigJs = tree.read(
|
||||
`libs/jest-preset-angular/jest.config.js`,
|
||||
'utf-8'
|
||||
);
|
||||
expect(jpaJestConfigJs).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should work with jest-preset-angular', async () => {
|
||||
await setup(tree, 'jest-preset-angular');
|
||||
tree.write(
|
||||
`libs/jest-preset-angular/jest.config.ts`,
|
||||
`export default {
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
stringifyContentPathRegex: '\\.(html|svg)$',
|
||||
}
|
||||
},
|
||||
transform: {
|
||||
'^.+.(ts|mjs|js|html)$': 'jest-preset-angular',
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js',
|
||||
};`
|
||||
);
|
||||
tree.write(
|
||||
`libs/jest-preset-angular/jest.config.js`,
|
||||
`module.exports = {
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
stringifyContentPathRegex: '\\.(html|svg)$',
|
||||
}
|
||||
},
|
||||
transform: {
|
||||
'^.+.(ts|mjs|js|html)$': 'jest-preset-angular',
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js',
|
||||
};`
|
||||
);
|
||||
await updateConfigsJest29(tree);
|
||||
const jpaJestConfigTs = tree.read(
|
||||
`libs/jest-preset-angular/jest.config.ts`,
|
||||
'utf-8'
|
||||
);
|
||||
expect(jpaJestConfigTs).toMatchSnapshot();
|
||||
const jpaJestConfigJs = tree.read(
|
||||
`libs/jest-preset-angular/jest.config.js`,
|
||||
'utf-8'
|
||||
);
|
||||
expect(jpaJestConfigJs).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should work if not using ts-jest transformer', async () => {
|
||||
await setup(tree, 'no-ts-jest');
|
||||
tree.write(
|
||||
`libs/no-ts-jest/jest.config.ts`,
|
||||
`export default {
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': 'babel-jest',
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js',
|
||||
};`
|
||||
);
|
||||
tree.write(
|
||||
`libs/no-ts-jest/jest.config.js`,
|
||||
`module.exports = {
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': 'babel-jest',
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js',
|
||||
};`
|
||||
);
|
||||
|
||||
await updateConfigsJest29(tree);
|
||||
const noTsJestConfigTs = tree.read(
|
||||
`libs/no-ts-jest/jest.config.ts`,
|
||||
'utf-8'
|
||||
);
|
||||
expect(noTsJestConfigTs).toMatchSnapshot();
|
||||
const noTsJestConfigJs = tree.read(
|
||||
`libs/no-ts-jest/jest.config.js`,
|
||||
'utf-8'
|
||||
);
|
||||
expect(noTsJestConfigJs).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should work snapshotFormat is defined', async () => {
|
||||
await setup(tree, 'no-ts-jest');
|
||||
tree.write(
|
||||
`libs/no-ts-jest/jest.config.ts`,
|
||||
`export default {
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': 'babel-jest',
|
||||
},
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json'
|
||||
},
|
||||
something: 'else',
|
||||
abc: [1234, true, {abc: 'yes'}]
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js',
|
||||
snapshotFormat: {escapeString: false, printBasicPrototype: true}
|
||||
};`
|
||||
);
|
||||
tree.write(
|
||||
`libs/no-ts-jest/jest.config.js`,
|
||||
`module.exports = {
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': 'ts-jest',
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html']
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js',
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json'
|
||||
},
|
||||
something: 'else',
|
||||
abc: [1234, true, {abc: 'yes'}]
|
||||
},
|
||||
snapshotFormat: {escapeString: false, printBasicPrototype: true}
|
||||
};`
|
||||
);
|
||||
|
||||
await updateConfigsJest29(tree);
|
||||
const snapshotJestConfigTs = tree.read(
|
||||
'libs/no-ts-jest/jest.config.ts',
|
||||
'utf-8'
|
||||
);
|
||||
expect(snapshotJestConfigTs).toMatchSnapshot();
|
||||
const snapshotJestConfigJs = tree.read(
|
||||
`libs/no-ts-jest/jest.config.js`,
|
||||
'utf-8'
|
||||
);
|
||||
expect(snapshotJestConfigJs).toMatchSnapshot();
|
||||
});
|
||||
it('should be idempotent', async () => {
|
||||
await setup(tree, 'my-lib');
|
||||
|
||||
await updateConfigsJest29(tree);
|
||||
|
||||
const actualJestConfigTs1 = tree.read(
|
||||
'libs/my-lib/jest.config.ts',
|
||||
'utf-8'
|
||||
);
|
||||
expect(actualJestConfigTs1).toMatchSnapshot();
|
||||
const actualJestConfigJs1 = tree.read(
|
||||
'libs/my-lib/jest.config.js',
|
||||
'utf-8'
|
||||
);
|
||||
expect(actualJestConfigJs1).toMatchSnapshot();
|
||||
|
||||
await updateConfigsJest29(tree);
|
||||
|
||||
const actualJestConfigTs2 = tree.read(
|
||||
'libs/my-lib/jest.config.ts',
|
||||
'utf-8'
|
||||
);
|
||||
expect(actualJestConfigTs2).toEqual(actualJestConfigTs1);
|
||||
const actualJestConfigJs2 = tree.read(
|
||||
'libs/my-lib/jest.config.js',
|
||||
'utf-8'
|
||||
);
|
||||
expect(actualJestConfigJs2).toEqual(actualJestConfigJs1);
|
||||
});
|
||||
});
|
||||
|
||||
async function setup(tree: Tree, name: string, existingGraph?: ProjectGraph) {
|
||||
await libraryGenerator(tree, {
|
||||
name,
|
||||
});
|
||||
const projectConfig = readProjectConfiguration(tree, name);
|
||||
projectConfig.targets['test'] = {
|
||||
...projectConfig.targets['test'],
|
||||
executor: '@nrwl/jest:jest',
|
||||
configurations: {
|
||||
ci: {
|
||||
ci: true,
|
||||
},
|
||||
other: {
|
||||
jestConfig: `libs/${name}/jest.config.js`,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
updateProjectConfiguration(tree, name, projectConfig);
|
||||
tree.write(
|
||||
`libs/${name}/jest.config.ts`,
|
||||
`/* eslint-disable */
|
||||
export default {
|
||||
displayName: '${name}',
|
||||
preset: '../../jest.preset.js',
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
}
|
||||
},
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': 'ts-jest'
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||
coverageDirectory: '../../coverage/libs/${name}'
|
||||
};
|
||||
`
|
||||
);
|
||||
|
||||
tree.write(
|
||||
`libs/${name}/jest.config.js`,
|
||||
`module.exports = {
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': 'ts-jest'
|
||||
},
|
||||
// I am a comment and shouldn't be removed
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json'
|
||||
},
|
||||
something: 'else',
|
||||
abc: [1234, true, {abc: 'yes'}]
|
||||
},
|
||||
/**
|
||||
* Multi-line comment shouldn't be removed
|
||||
*/
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js'
|
||||
};
|
||||
`
|
||||
);
|
||||
|
||||
tree.write(
|
||||
'jest.config.ts',
|
||||
tree
|
||||
.read('jest.config.ts')
|
||||
.toString()
|
||||
.replace(new RegExp('@nx/jest', 'g'), '@nrwl/jest')
|
||||
);
|
||||
|
||||
tree.write(
|
||||
'jest.preset.js',
|
||||
tree
|
||||
.read('jest.preset.js')
|
||||
.toString()
|
||||
.replace(new RegExp('@nx/jest', 'g'), '@nrwl/jest')
|
||||
);
|
||||
|
||||
projectGraph = {
|
||||
dependencies: {
|
||||
...existingGraph?.dependencies,
|
||||
},
|
||||
nodes: {
|
||||
...existingGraph?.nodes,
|
||||
[name]: {
|
||||
name,
|
||||
type: 'lib',
|
||||
data: projectConfig,
|
||||
} as any,
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -1,230 +0,0 @@
|
||||
import {
|
||||
createProjectGraphAsync,
|
||||
formatFiles,
|
||||
logger,
|
||||
stripIndents,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
import { forEachExecutorOptionsInGraph } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { TS_QUERY_JEST_CONFIG_PREFIX } from '../../utils/ast-utils';
|
||||
import { tsquery } from '@phenomnomnominal/tsquery';
|
||||
import * as ts from 'typescript';
|
||||
import { JestExecutorOptions } from '../../executors/jest/schema';
|
||||
import { findRootJestPreset } from '../../utils/config/find-root-jest-files';
|
||||
|
||||
export async function updateConfigsJest29(tree: Tree) {
|
||||
const rootPreset = findRootJestPreset(tree);
|
||||
const targetsWithJest = new Set<string>();
|
||||
// have to use graph so the negative configuration targets are expanded
|
||||
const graph = await createProjectGraphAsync();
|
||||
forEachExecutorOptionsInGraph<JestExecutorOptions>(
|
||||
graph,
|
||||
'@nrwl/jest:jest',
|
||||
(options, projectName, targetName) => {
|
||||
if (options.jestConfig && tree.exists(options.jestConfig)) {
|
||||
targetsWithJest.add(targetName);
|
||||
// if the default root preset exists or if the project doesn't have a 'preset' configured
|
||||
// -> update snapshot config
|
||||
if (!rootPreset || !hasPresetConfigured(tree, options.jestConfig)) {
|
||||
addSnapshotOptionsToConfig(
|
||||
tree,
|
||||
options.jestConfig,
|
||||
`From within the project directory, run "nx test --update-snapshot"`
|
||||
);
|
||||
}
|
||||
updateTsJestOptions(tree, options.jestConfig);
|
||||
updateNgJestOptions(tree, options.jestConfig);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (rootPreset && tree.exists(rootPreset)) {
|
||||
const cmd = `"nx affected --targets=${Array.from(targetsWithJest).join(
|
||||
','
|
||||
)} --update-snapshot"`;
|
||||
addSnapshotOptionsToConfig(tree, rootPreset, cmd);
|
||||
updateTsJestOptions(tree, rootPreset);
|
||||
updateNgJestOptions(tree, rootPreset);
|
||||
}
|
||||
|
||||
await formatFiles(tree);
|
||||
logger.info(stripIndents`NX Jest Snapshot format changed in v29.
|
||||
By default Nx kept the older style to prevent breaking of existing tests with snapshots.
|
||||
It's recommend you update to the latest format.
|
||||
You can do this in your project's jest config file.
|
||||
Remove the snapshotFormat property and re-run tests with the --update-snapshot flag.
|
||||
More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format`);
|
||||
}
|
||||
|
||||
function addSnapshotOptionsToConfig(
|
||||
tree: Tree,
|
||||
configPath: string,
|
||||
updateSnapshotExample: string
|
||||
) {
|
||||
const config = tree.read(configPath, 'utf-8');
|
||||
const hasSnapshotOptions = tsquery.query(
|
||||
config,
|
||||
`${TS_QUERY_JEST_CONFIG_PREFIX} > ObjectLiteralExpression PropertyAssignment:has(Identifier[name="snapshotFormat"])`
|
||||
);
|
||||
if (hasSnapshotOptions.length > 0) {
|
||||
return;
|
||||
}
|
||||
const updatedConfig = tsquery.replace(
|
||||
config,
|
||||
`${TS_QUERY_JEST_CONFIG_PREFIX} > ObjectLiteralExpression`,
|
||||
(node: ts.ObjectLiteralExpression) => {
|
||||
return `{
|
||||
${node.properties.map((p) => getNodeWithComments(config, p)).join(',\n')},
|
||||
/* TODO: Update to latest Jest snapshotFormat
|
||||
* By default Nx has kept the older style of Jest Snapshot formats
|
||||
* to prevent breaking of any existing tests with snapshots.
|
||||
* It's recommend you update to the latest format.
|
||||
* You can do this by removing snapshotFormat property
|
||||
* and running tests with --update-snapshot flag.
|
||||
* Example: ${updateSnapshotExample}
|
||||
* More info: https://jestjs.io/docs/upgrading-to-jest29#snapshot-format
|
||||
*/
|
||||
snapshotFormat: { escapeString: true, printBasicPrototype: true }
|
||||
}`;
|
||||
},
|
||||
{ visitAllChildren: false }
|
||||
);
|
||||
|
||||
tree.write(configPath, updatedConfig);
|
||||
}
|
||||
|
||||
function hasPresetConfigured(tree: Tree, configPath: string): boolean {
|
||||
const contents = tree.read(configPath, 'utf-8');
|
||||
|
||||
return (
|
||||
tsquery.query(
|
||||
contents,
|
||||
`${TS_QUERY_JEST_CONFIG_PREFIX} > ObjectLiteralExpression PropertyAssignment:has(Identifier[name="preset"])`
|
||||
)?.length > 0
|
||||
);
|
||||
}
|
||||
|
||||
function updateTsJestOptions(tree: Tree, configPath: string) {
|
||||
// query for the globals property, if they don't have one then there's nothing to modify.
|
||||
const contents = tree.read(configPath, 'utf-8');
|
||||
let tsJestGlobalsConfig: string;
|
||||
const noTsJestGlobals = tsquery.replace(
|
||||
contents,
|
||||
`${TS_QUERY_JEST_CONFIG_PREFIX} > ObjectLiteralExpression PropertyAssignment:has(Identifier[name="globals"])`,
|
||||
(node: ts.PropertyAssignment) => {
|
||||
if (tsJestGlobalsConfig) {
|
||||
logger.warn(
|
||||
stripIndents`Found more than one "globals" object in the jest config, ${configPath}
|
||||
Will use the first one`
|
||||
);
|
||||
return;
|
||||
}
|
||||
tsJestGlobalsConfig = getGlobalTsJestConfig(node);
|
||||
return getGlobalConfigWithoutTsJest(node);
|
||||
}
|
||||
);
|
||||
|
||||
if (!tsJestGlobalsConfig) {
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedTsJestTransformer = tsquery.replace(
|
||||
noTsJestGlobals,
|
||||
`${TS_QUERY_JEST_CONFIG_PREFIX}> ObjectLiteralExpression PropertyAssignment:has(Identifier[name="transform"]) PropertyAssignment > :has(StringLiteral[value="ts-jest"], StringLiteral[value="jest-preset-angular"])`,
|
||||
(node: ts.StringLiteral) => {
|
||||
return `[${node.getText()}, ${tsJestGlobalsConfig}]`;
|
||||
}
|
||||
);
|
||||
|
||||
tree.write(configPath, updatedTsJestTransformer);
|
||||
}
|
||||
|
||||
function updateNgJestOptions(tree: Tree, configPath: string) {
|
||||
const contents = tree.read(configPath, 'utf-8');
|
||||
|
||||
let ngJestTeardownConfig: string;
|
||||
const noTeardownConfig = tsquery.replace(
|
||||
contents,
|
||||
'BinaryExpression:has(PropertyAccessExpression:has(Identifier[name=ngJest])) PropertyAssignment:has(Identifier[name=teardown])',
|
||||
(node: ts.PropertyAssignment) => {
|
||||
ngJestTeardownConfig = node.initializer.getText();
|
||||
return ' ';
|
||||
}
|
||||
);
|
||||
|
||||
if (!ngJestTeardownConfig) {
|
||||
return;
|
||||
}
|
||||
|
||||
let maybeUpdatedTestEnvOpts = tsquery.replace(
|
||||
noTeardownConfig,
|
||||
`${TS_QUERY_JEST_CONFIG_PREFIX} > ObjectLiteralExpression PropertyAssignment:has(Identifier[name="testEnvironmentOptions"]) ObjectLiteralExpression`,
|
||||
(node: ts.ObjectLiteralExpression) => {
|
||||
return `{
|
||||
${node.properties
|
||||
.map((p) => getNodeWithComments(noTeardownConfig, p))
|
||||
.join(',\n')},
|
||||
teardown: ${ngJestTeardownConfig}
|
||||
}`;
|
||||
}
|
||||
);
|
||||
|
||||
if (maybeUpdatedTestEnvOpts !== noTeardownConfig) {
|
||||
tree.write(configPath, maybeUpdatedTestEnvOpts);
|
||||
return;
|
||||
}
|
||||
// didn't find existing testEnvironmentOptions, so add the new property
|
||||
|
||||
const updatedConfig = tsquery.replace(
|
||||
maybeUpdatedTestEnvOpts,
|
||||
`${TS_QUERY_JEST_CONFIG_PREFIX} > ObjectLiteralExpression`,
|
||||
(node: ts.ObjectLiteralExpression) => {
|
||||
return `{
|
||||
${node.properties
|
||||
.map((p) => getNodeWithComments(maybeUpdatedTestEnvOpts, p))
|
||||
.join(',\n')},
|
||||
testEnvironmentOptions: { teardown: ${ngJestTeardownConfig} },
|
||||
}`;
|
||||
},
|
||||
{ visitAllChildren: false }
|
||||
);
|
||||
tree.write(configPath, updatedConfig);
|
||||
}
|
||||
|
||||
function getGlobalTsJestConfig(node: ts.PropertyAssignment): string {
|
||||
const globalObject = node.initializer as ts.ObjectLiteralExpression;
|
||||
const foundConfig = globalObject.properties.find(
|
||||
(p) => ts.isPropertyAssignment(p) && p.name.getText().includes('ts-jest')
|
||||
) as ts.PropertyAssignment;
|
||||
|
||||
return foundConfig?.initializer?.getText() || '';
|
||||
}
|
||||
|
||||
function getGlobalConfigWithoutTsJest(node: ts.PropertyAssignment): string {
|
||||
const globalObject = node?.initializer as ts.ObjectLiteralExpression;
|
||||
const withoutTsJest = globalObject?.properties?.filter((p) => {
|
||||
return !(
|
||||
ts.isPropertyAssignment(p) && p.name.getText().includes('ts-jest')
|
||||
);
|
||||
});
|
||||
|
||||
const globalConfigs = withoutTsJest.map((c) => c.getText()).join(',\n');
|
||||
return `globals: { ${globalConfigs} }`;
|
||||
}
|
||||
|
||||
function getNodeWithComments(fullText: string, node: ts.Node) {
|
||||
const commentRanges = ts.getLeadingCommentRanges(
|
||||
fullText,
|
||||
node.getFullStart()
|
||||
);
|
||||
|
||||
if (commentRanges?.length > 0) {
|
||||
const withComments = `${commentRanges
|
||||
.map((r) => fullText.slice(r.pos, r.end))
|
||||
.join('\n')}\n${node.getText()}`;
|
||||
return withComments;
|
||||
}
|
||||
return node.getText();
|
||||
}
|
||||
|
||||
export default updateConfigsJest29;
|
||||
@ -1,182 +0,0 @@
|
||||
import {
|
||||
ProjectGraph,
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { libraryGenerator } from '@nx/js';
|
||||
import { updateTestsJest29 } from './update-tests-jest-29';
|
||||
|
||||
let projectGraph: ProjectGraph;
|
||||
jest.mock('@nx/devkit', () => ({
|
||||
...jest.requireActual<any>('@nx/devkit'),
|
||||
createProjectGraphAsync: jest
|
||||
.fn()
|
||||
.mockImplementation(async () => projectGraph),
|
||||
}));
|
||||
describe('Jest Migration - jest 29 mocked usage in tests', () => {
|
||||
let tree: Tree;
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
});
|
||||
|
||||
it('should not update anything if there are no tests', async () => {
|
||||
await setup(tree, 'my-lib');
|
||||
const expected = tree.read('libs/my-lib/src/lib/my-lib.spec.ts', 'utf-8');
|
||||
await updateTestsJest29(tree);
|
||||
expect(
|
||||
tree.read('libs/my-lib/src/file-one.spec.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('libs/my-lib/src/file-two.spec.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(tree.read('libs/my-lib/src/lib/my-lib.spec.ts', 'utf-8')).toEqual(
|
||||
expected
|
||||
);
|
||||
});
|
||||
it('should be idempotent', async () => {
|
||||
await setup(tree, 'my-lib');
|
||||
|
||||
const expected = tree.read('libs/my-lib/src/lib/my-lib.spec.ts', 'utf-8');
|
||||
|
||||
await updateTestsJest29(tree);
|
||||
|
||||
expect(
|
||||
tree.read('libs/my-lib/src/file-one.spec.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('libs/my-lib/src/file-two.spec.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(tree.read('libs/my-lib/src/lib/my-lib.spec.ts', 'utf-8')).toEqual(
|
||||
expected
|
||||
);
|
||||
|
||||
await updateTestsJest29(tree);
|
||||
|
||||
expect(
|
||||
tree.read('libs/my-lib/src/file-one.spec.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(
|
||||
tree.read('libs/my-lib/src/file-two.spec.ts', 'utf-8')
|
||||
).toMatchSnapshot();
|
||||
expect(tree.read('libs/my-lib/src/lib/my-lib.spec.ts', 'utf-8')).toEqual(
|
||||
expected
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
async function setup(tree: Tree, name: string) {
|
||||
await libraryGenerator(tree, {
|
||||
name,
|
||||
});
|
||||
const projectConfig = readProjectConfiguration(tree, name);
|
||||
projectConfig.targets['test'] = {
|
||||
...projectConfig.targets['test'],
|
||||
executor: '@nrwl/jest:jest',
|
||||
configurations: {
|
||||
ci: {
|
||||
ci: true,
|
||||
},
|
||||
other: {
|
||||
jestConfig: `libs/${name}/jest.config.js`,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
updateProjectConfiguration(tree, name, projectConfig);
|
||||
|
||||
tree.write(
|
||||
`libs/${name}/jest.config.js`,
|
||||
`module.exports = {
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': 'ts-jest'
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'html'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json'
|
||||
}
|
||||
},
|
||||
displayName: 'jest',
|
||||
testEnvironment: 'node',
|
||||
preset: '../../jest.preset.js'
|
||||
};
|
||||
|
||||
`
|
||||
);
|
||||
tree.write(
|
||||
`libs/${name}/src/file-one.spec.ts`,
|
||||
`
|
||||
import{ MaybeMockedDeep, MaybeMocked } from 'jest-mock';
|
||||
import {expect, jest, test} from '@jest/globals';
|
||||
import {song} from './song';
|
||||
|
||||
jest.mock('./song');
|
||||
jest.spyOn(console, 'log');
|
||||
|
||||
const mockedSong = jest.mocked(song, true);
|
||||
// or through \`jest.Mocked<Source>\`
|
||||
// const mockedSong = song as jest.Mocked<typeof song>;
|
||||
|
||||
test('deep method is typed correctly', () => {
|
||||
mockedSong.one.more.time.mockReturnValue(12);
|
||||
|
||||
expect(mockedSong.one.more.time(10)).toBe(12);
|
||||
expect(mockedSong.one.more.time.mock.calls).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('direct usage', () => {
|
||||
jest.mocked(console.log).mockImplementation(() => {
|
||||
return;
|
||||
});
|
||||
|
||||
console.log('one more time');
|
||||
|
||||
expect(jest.mocked(console.log, false).mock.calls).toHaveLength(1);
|
||||
});
|
||||
`
|
||||
);
|
||||
tree.write(
|
||||
`libs/${name}/src/file-two.spec.ts`,
|
||||
`
|
||||
const { MaybeMockedDeep, MaybeMocked } = require('jest-mock');
|
||||
const {expect, jest, test} = require('@jest/globals');
|
||||
const {song} = require('./song');
|
||||
|
||||
jest.mock('./song');
|
||||
jest.spyOn(console, 'log');
|
||||
|
||||
const mockedSong = jest.mocked(song, true);
|
||||
// or through \`jest.Mocked<Source>\`
|
||||
// const mockedSong = song as jest.Mocked<typeof song>;
|
||||
|
||||
test('deep method is typed correctly', () => {
|
||||
mockedSong.one.more.time.mockReturnValue(12);
|
||||
|
||||
expect(mockedSong.one.more.time(10)).toBe(12);
|
||||
expect(mockedSong.one.more.time.mock.calls).toHaveLength(1);
|
||||
});
|
||||
|
||||
test('direct usage', () => {
|
||||
jest.mocked(console.log).mockImplementation(() => {
|
||||
return;
|
||||
});
|
||||
|
||||
console.log('one more time');
|
||||
|
||||
expect(jest.mocked(console.log, false).mock.calls).toHaveLength(1);
|
||||
});
|
||||
`
|
||||
);
|
||||
projectGraph = {
|
||||
dependencies: {},
|
||||
nodes: {
|
||||
[name]: {
|
||||
name,
|
||||
type: 'lib',
|
||||
data: projectConfig,
|
||||
} as any,
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -1,96 +0,0 @@
|
||||
import {
|
||||
createProjectGraphAsync,
|
||||
formatFiles,
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
visitNotIgnoredFiles,
|
||||
} from '@nx/devkit';
|
||||
import { forEachExecutorOptionsInGraph } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { tsquery } from '@phenomnomnominal/tsquery';
|
||||
import {
|
||||
CallExpression,
|
||||
ImportDeclaration,
|
||||
VariableStatement,
|
||||
} from 'typescript';
|
||||
import type { JestExecutorOptions } from '../../executors/jest/schema';
|
||||
import { TEST_FILE_PATTERN } from '../../utils/ast-utils';
|
||||
|
||||
export async function updateTestsJest29(tree: Tree) {
|
||||
const graph = await createProjectGraphAsync();
|
||||
forEachExecutorOptionsInGraph<JestExecutorOptions>(
|
||||
graph,
|
||||
'@nrwl/jest:jest',
|
||||
(options, projectName) => {
|
||||
const projectConfig = readProjectConfiguration(tree, projectName);
|
||||
visitNotIgnoredFiles(
|
||||
tree,
|
||||
projectConfig.sourceRoot || projectConfig.root,
|
||||
(file) => {
|
||||
if (!TEST_FILE_PATTERN.test(file)) {
|
||||
return;
|
||||
}
|
||||
updateJestMockTypes(tree, file);
|
||||
updateJestMocked(tree, file);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
export function updateJestMockTypes(tree: Tree, filePath: string) {
|
||||
const contents = tree.read(filePath, 'utf-8');
|
||||
const updatedContent = tsquery.replace(
|
||||
contents,
|
||||
':matches(ImportDeclaration, VariableStatement):has(Identifier[name="MaybeMockedDeep"], Identifier[name="MaybeMocked"]):has(StringLiteral[value="jest-mock"])',
|
||||
(node: ImportDeclaration | VariableStatement) => {
|
||||
const text = node.getText();
|
||||
return (
|
||||
text
|
||||
// MaybeMockedDeep and MaybeMocked now are exported as Mocked and MockedShallow
|
||||
.replace('MaybeMockedDeep', 'Mocked')
|
||||
.replace('MaybeMocked', 'MockedShallow')
|
||||
);
|
||||
}
|
||||
);
|
||||
tree.write(filePath, updatedContent);
|
||||
}
|
||||
|
||||
export function updateJestMocked(tree: Tree, filePath: string) {
|
||||
const contents = tree.read(filePath, 'utf-8');
|
||||
const jestGlobalNodes = tsquery.query(
|
||||
contents,
|
||||
':matches(ImportDeclaration, VariableStatement):has(Identifier[name="jest"]):has(StringLiteral[value="@jest/globals"])'
|
||||
);
|
||||
|
||||
// this only applies if using jest from @jest/globals
|
||||
if (jestGlobalNodes.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const updatedJestMockTypes = tsquery.replace(
|
||||
contents,
|
||||
'CallExpression:has(Identifier[name="jest"]):has(Identifier[name="mocked"])',
|
||||
(node: CallExpression) => {
|
||||
if (
|
||||
node.arguments.length === 2 &&
|
||||
node.getText().startsWith('jest.mocked(')
|
||||
) {
|
||||
const text = node.getText();
|
||||
// jest.mocked(someObject, true); => jest.mocked(someObject);
|
||||
if (node.arguments[1].getText() === 'true') {
|
||||
return text.replace(/,\s*true/g, '');
|
||||
}
|
||||
// jest.mocked(someObject, false); => jest.mocked(someObject, {shallow: true});
|
||||
// opt into the new behavior unless explicitly opting out
|
||||
if (node.arguments[1].getText() === 'false') {
|
||||
return text.replace('false', '{shallow: true}');
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
tree.write(filePath, updatedJestMockTypes);
|
||||
}
|
||||
|
||||
export default updateTestsJest29;
|
||||
@ -1,11 +1,5 @@
|
||||
{
|
||||
"generators": {
|
||||
"rename-swcrc-config": {
|
||||
"cli": "nx",
|
||||
"version": "15.8.0-beta.0",
|
||||
"description": "Rename .lib.swcrc to .swcrc for better SWC support throughout the workspace",
|
||||
"factory": "./src/migrations/update-15-8-0/rename-swcrc-config"
|
||||
},
|
||||
"update-16-0-0-add-nx-packages": {
|
||||
"cli": "nx",
|
||||
"version": "16.0.0-beta.1",
|
||||
@ -32,18 +26,6 @@
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
"15.8.0": {
|
||||
"version": "15.8.0-beta.2",
|
||||
"x-prompt": "Do you want to update to TypeScript v4.9?",
|
||||
"requires": {
|
||||
"typescript": ">=4.8.2 <4.9.0"
|
||||
},
|
||||
"packages": {
|
||||
"typescript": {
|
||||
"version": "~4.9.5"
|
||||
}
|
||||
}
|
||||
},
|
||||
"16.0.0": {
|
||||
"version": "16.0.0-beta.3",
|
||||
"packages": {
|
||||
|
||||
@ -37,7 +37,6 @@
|
||||
"@babel/preset-env": "^7.23.2",
|
||||
"@babel/preset-typescript": "^7.22.5",
|
||||
"@babel/runtime": "^7.22.6",
|
||||
"@phenomnomnominal/tsquery": "~5.0.1",
|
||||
"babel-plugin-const-enum": "^1.0.1",
|
||||
"babel-plugin-macros": "^2.8.0",
|
||||
"babel-plugin-transform-typescript-metadata": "^0.3.1",
|
||||
|
||||
@ -1,119 +0,0 @@
|
||||
import 'nx/src/internal-testing-utils/mock-project-graph';
|
||||
|
||||
import {
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { libraryGenerator } from '../../generators/library/library';
|
||||
import renameSwcrcConfig from './rename-swcrc-config';
|
||||
|
||||
describe('Rename swcrc file migration', () => {
|
||||
let tree: Tree;
|
||||
|
||||
let originalEnv: string;
|
||||
beforeEach(() => {
|
||||
originalEnv = process.env.NX_ADD_PLUGINS;
|
||||
process.env.NX_ADD_PLUGINS = 'false';
|
||||
});
|
||||
afterEach(() => {
|
||||
process.env.NX_ADD_PLUGINS = originalEnv;
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
});
|
||||
|
||||
it('should migrate .lib.swcrc to .swcrc', async () => {
|
||||
await setup(tree);
|
||||
await renameSwcrcConfig(tree);
|
||||
|
||||
expect(tree.exists('libs/my-lib/.lib.swcrc')).toBeFalsy();
|
||||
expect(tree.exists('libs/my-lib/.swcrc')).toBeTruthy();
|
||||
|
||||
const jestConfig = tree.read('libs/my-lib/jest.config.ts', 'utf-8');
|
||||
expect(jestConfig).toContain(`fs.readFileSync(\`\${__dirname}/.swcrc\``);
|
||||
expect(jestConfig).toContain('swcJestConfig.swcrc = false');
|
||||
});
|
||||
|
||||
it('should migrate custom path .lib.swcrc', async () => {
|
||||
await setup(tree);
|
||||
customSwcrcPath(tree);
|
||||
|
||||
await renameSwcrcConfig(tree);
|
||||
|
||||
expect(tree.exists('libs/my-lib/src/.lib.swcrc')).toBeFalsy();
|
||||
expect(tree.exists('libs/my-lib/src/.swcrc')).toBeTruthy();
|
||||
|
||||
const projectConfig = readProjectConfiguration(tree, 'my-lib');
|
||||
expect(projectConfig.targets.build.options.swcrc).toEqual(
|
||||
'libs/my-lib/src/.swcrc'
|
||||
);
|
||||
});
|
||||
|
||||
it('should do nothing if custom path swcrc is not named .lib.swcrc', async () => {
|
||||
await setup(tree);
|
||||
customSwcrcPath(tree, true);
|
||||
|
||||
await renameSwcrcConfig(tree);
|
||||
|
||||
expect(tree.exists('libs/my-lib/src/.custom.swcrc')).toBeTruthy();
|
||||
expect(tree.exists('libs/my-lib/src/.swcrc')).toBeFalsy();
|
||||
|
||||
const projectConfig = readProjectConfiguration(tree, 'my-lib');
|
||||
expect(projectConfig.targets.build.options.swcrc).toEqual(
|
||||
'libs/my-lib/src/.custom.swcrc'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
async function setup(tree: Tree) {
|
||||
await libraryGenerator(tree, {
|
||||
name: 'my-lib',
|
||||
bundler: 'swc',
|
||||
unitTestRunner: 'jest',
|
||||
config: 'project',
|
||||
});
|
||||
|
||||
const projectConfig = readProjectConfiguration(tree, 'my-lib');
|
||||
projectConfig.targets.build.executor = '@nrwl/js:swc';
|
||||
projectConfig.targets.test.executor = '@nrwl/jest:jest';
|
||||
updateProjectConfiguration(tree, 'my-lib', projectConfig);
|
||||
|
||||
tree.rename('libs/my-lib/.swcrc', 'libs/my-lib/.lib.swcrc');
|
||||
tree.write(
|
||||
'libs/my-lib/jest.config.ts',
|
||||
`
|
||||
const fs = require('fs');
|
||||
|
||||
// Reading the SWC compilation config and remove the "exclude"
|
||||
// for the test files to be compiled by SWC
|
||||
const { exclude: _, ...swcJestConfig } = JSON.parse(
|
||||
fs.readFileSync(\`\${__dirname}/.lib.swcrc\`, 'utf-8')
|
||||
);
|
||||
|
||||
module.exports = {
|
||||
displayName: 'my-lib',
|
||||
preset: '../../jest.preset.js',
|
||||
transform: {
|
||||
'^.+\\.[tj]s$': ['@swc/jest', swcJestConfig],
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'js', 'html'],
|
||||
coverageDirectory: '../../coverage/packages/vite',
|
||||
};
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
function customSwcrcPath(tree: Tree, custom = false) {
|
||||
tree.rename(
|
||||
'libs/my-lib/.lib.swcrc',
|
||||
custom ? 'libs/my-lib/src/.custom.swcrc' : 'libs/my-lib/src/.lib.swcrc'
|
||||
);
|
||||
const projectConfig = readProjectConfiguration(tree, 'my-lib');
|
||||
projectConfig.targets.build.options.swcrc = custom
|
||||
? 'libs/my-lib/src/.custom.swcrc'
|
||||
: 'libs/my-lib/src/.lib.swcrc';
|
||||
updateProjectConfiguration(tree, 'my-lib', projectConfig);
|
||||
}
|
||||
@ -1,119 +0,0 @@
|
||||
import {
|
||||
formatFiles,
|
||||
getProjects,
|
||||
joinPathFragments,
|
||||
Tree,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { SwcExecutorOptions } from '../../utils/schema';
|
||||
import { tsquery } from '@phenomnomnominal/tsquery';
|
||||
import type { TemplateSpan } from 'typescript';
|
||||
|
||||
export default async function (tree: Tree) {
|
||||
let changesMade = false;
|
||||
const projects = getProjects(tree);
|
||||
|
||||
forEachExecutorOptions(
|
||||
tree,
|
||||
'@nrwl/js:swc',
|
||||
(_, projectName, target, configurationName) => {
|
||||
const projectConfiguration = projects.get(projectName);
|
||||
const executorOptions: SwcExecutorOptions = configurationName
|
||||
? projectConfiguration.targets[target].configurations[configurationName]
|
||||
: projectConfiguration.targets[target].options;
|
||||
// if the project uses a custom path to swcrc file
|
||||
// and only if it's the default name
|
||||
if (
|
||||
executorOptions.swcrc &&
|
||||
executorOptions.swcrc.includes('.lib.swcrc')
|
||||
) {
|
||||
const newSwcrc = executorOptions.swcrc.replace('.lib.swcrc', '.swcrc');
|
||||
// rename the swcrc file first
|
||||
tree.rename(executorOptions.swcrc, newSwcrc);
|
||||
// then update the executor options
|
||||
executorOptions.swcrc = newSwcrc;
|
||||
changesMade = true;
|
||||
}
|
||||
|
||||
const libSwcrcPath =
|
||||
joinPathFragments(projectConfiguration.root, '.lib.swcrc') ||
|
||||
joinPathFragments(projectConfiguration.sourceRoot, '.lib.swcrc');
|
||||
const isLibSwcrcExist = tree.exists(libSwcrcPath);
|
||||
|
||||
if (isLibSwcrcExist) {
|
||||
tree.rename(libSwcrcPath, libSwcrcPath.replace('.lib.swcrc', '.swcrc'));
|
||||
changesMade = true;
|
||||
}
|
||||
|
||||
updateProjectConfiguration(tree, projectName, projectConfiguration);
|
||||
}
|
||||
);
|
||||
|
||||
forEachExecutorOptions(
|
||||
tree,
|
||||
'@nrwl/jest:jest',
|
||||
(_, projectName, target, configurationName) => {
|
||||
const projectConfiguration = projects.get(projectName);
|
||||
const executorOptions = configurationName
|
||||
? projectConfiguration.targets[target].configurations[configurationName]
|
||||
: projectConfiguration.targets[target].options;
|
||||
|
||||
const isJestConfigExist =
|
||||
executorOptions.jestConfig && tree.exists(executorOptions.jestConfig);
|
||||
|
||||
if (isJestConfigExist) {
|
||||
const jestConfig = tree.read(executorOptions.jestConfig, 'utf-8');
|
||||
|
||||
const jsonParseNodes = tsquery.query(
|
||||
jestConfig,
|
||||
':matches(CallExpression:has(Identifier[name="JSON"]):has(Identifier[name="parse"]))'
|
||||
);
|
||||
|
||||
if (jsonParseNodes.length) {
|
||||
// if we already assign false to swcrc, skip
|
||||
if (jestConfig.includes('.swcrc = false')) {
|
||||
return;
|
||||
}
|
||||
|
||||
let updatedJestConfig = tsquery.replace(
|
||||
jestConfig,
|
||||
'CallExpression:has(Identifier[name="JSON"]):has(Identifier[name="parse"]) TemplateSpan',
|
||||
(templateSpan: TemplateSpan) => {
|
||||
if (templateSpan.literal.text === '/.lib.swcrc') {
|
||||
return templateSpan
|
||||
.getFullText()
|
||||
.replace('.lib.swcrc', '.swcrc');
|
||||
}
|
||||
return '';
|
||||
}
|
||||
);
|
||||
|
||||
updatedJestConfig = tsquery.replace(
|
||||
updatedJestConfig,
|
||||
':matches(ExportAssignment, BinaryExpression:has(Identifier[name="module"]):has(Identifier[name="exports"]))',
|
||||
(node) => {
|
||||
return `
|
||||
|
||||
// disable .swcrc look-up by SWC core because we're passing in swcJestConfig ourselves.
|
||||
// If we do not disable this, SWC Core will read .swcrc and won't transform our test files due to "exclude"
|
||||
if (swcJestConfig.swcrc === undefined) {
|
||||
swcJestConfig.swcrc = false;
|
||||
}
|
||||
|
||||
${node.getFullText()}
|
||||
`;
|
||||
}
|
||||
);
|
||||
|
||||
tree.write(executorOptions.jestConfig, updatedJestConfig);
|
||||
changesMade = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (changesMade) {
|
||||
await formatFiles(tree);
|
||||
}
|
||||
}
|
||||
@ -1,11 +1,5 @@
|
||||
{
|
||||
"generators": {
|
||||
"add-style-packages": {
|
||||
"cli": "nx",
|
||||
"version": "15.8.8-beta.0",
|
||||
"description": "Add less and stylus packages if used.",
|
||||
"factory": "./src/migrations/update-15-8-8/add-style-packages"
|
||||
},
|
||||
"update-16-0-0-add-nx-packages": {
|
||||
"cli": "nx",
|
||||
"version": "16.0.0-beta.1",
|
||||
@ -32,40 +26,6 @@
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
"15.0.4": {
|
||||
"version": "15.0.4-beta.0",
|
||||
"packages": {
|
||||
"next": {
|
||||
"version": "13.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"eslint-config-next": {
|
||||
"version": "13.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"less-loader": {
|
||||
"version": "11.1.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"stylus-loader": {
|
||||
"version": "7.1.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"15.4.5": {
|
||||
"version": "15.4.5-beta.0",
|
||||
"packages": {
|
||||
"next": {
|
||||
"version": "13.1.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"eslint-config-next": {
|
||||
"version": "13.1.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"16.0.0": {
|
||||
"version": "16.0.0-beta.0",
|
||||
"packages": {
|
||||
|
||||
@ -16,7 +16,7 @@ import {
|
||||
writeFileSync,
|
||||
} from 'fs-extra';
|
||||
import { dirname, extname, join, relative } from 'path';
|
||||
import { findNodes } from 'nx/src/utils/typescript';
|
||||
import { findNodes } from '@nx/js';
|
||||
|
||||
import type { NextBuildBuilderOptions } from '../../../utils/types';
|
||||
|
||||
|
||||
@ -1,85 +0,0 @@
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { writeJson, readJson, Tree, addProjectConfiguration } from '@nx/devkit';
|
||||
import update from './add-style-packages';
|
||||
|
||||
describe('Add less if needed', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace();
|
||||
});
|
||||
|
||||
it('should add less if used', async () => {
|
||||
writeJson(tree, 'package.json', {
|
||||
dependencies: {},
|
||||
devDependencies: {},
|
||||
});
|
||||
addProjectConfiguration(tree, 'myapp', {
|
||||
root: 'myapp',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/next:build',
|
||||
},
|
||||
},
|
||||
});
|
||||
tree.write(
|
||||
`myapp/next.config.js`,
|
||||
`require('@nrwl/next/plugins/with-less')`
|
||||
);
|
||||
|
||||
await update(tree);
|
||||
|
||||
const packageJson = readJson(tree, 'package.json');
|
||||
expect(packageJson).toEqual({
|
||||
dependencies: {},
|
||||
devDependencies: { less: '3.12.2' },
|
||||
});
|
||||
});
|
||||
|
||||
it('should not add anything if less not used by Next.js app', async () => {
|
||||
writeJson(tree, 'package.json', {
|
||||
dependencies: {},
|
||||
devDependencies: {},
|
||||
});
|
||||
addProjectConfiguration(tree, 'myapp', {
|
||||
root: 'myapp',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/next:build',
|
||||
},
|
||||
},
|
||||
});
|
||||
tree.write(`myapp/next.config.js`, `require('@nrwl/next/plugins/with-nx')`);
|
||||
|
||||
await update(tree);
|
||||
|
||||
const packageJson = readJson(tree, 'package.json');
|
||||
expect(packageJson).toEqual({
|
||||
dependencies: {},
|
||||
devDependencies: {},
|
||||
});
|
||||
});
|
||||
|
||||
it('should not add anything if no Next.js apps are in workspace', async () => {
|
||||
writeJson(tree, 'package.json', {
|
||||
dependencies: {},
|
||||
devDependencies: {},
|
||||
});
|
||||
addProjectConfiguration(tree, 'myapp', {
|
||||
root: 'myapp',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/webpack:webpack',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await update(tree);
|
||||
|
||||
const packageJson = readJson(tree, 'package.json');
|
||||
expect(packageJson).toEqual({
|
||||
dependencies: {},
|
||||
devDependencies: {},
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,35 +0,0 @@
|
||||
import {
|
||||
addDependenciesToPackageJson,
|
||||
getProjects,
|
||||
joinPathFragments,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
|
||||
export async function update(tree: Tree) {
|
||||
const projects = getProjects(tree);
|
||||
const missingDeps = {};
|
||||
|
||||
for (const [, config] of projects) {
|
||||
if (
|
||||
config.targets?.build?.executor === '@nrwl/next:build' &&
|
||||
tree.exists(joinPathFragments(config.root, 'next.config.js'))
|
||||
) {
|
||||
const nextConfigContent = tree.read(
|
||||
joinPathFragments(config.root, 'next.config.js'),
|
||||
'utf-8'
|
||||
);
|
||||
|
||||
if (nextConfigContent.includes('@nrwl/next/plugins/with-less')) {
|
||||
missingDeps['less'] = '3.12.2';
|
||||
}
|
||||
|
||||
if (nextConfigContent.includes('@nrwl/next/plugins/with-stylus')) {
|
||||
missingDeps['stylus'] = '^0.55.0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return addDependenciesToPackageJson(tree, {}, missingDeps);
|
||||
}
|
||||
|
||||
export default update;
|
||||
@ -1,29 +1,5 @@
|
||||
{
|
||||
"generators": {
|
||||
"15.0.0-migrate-to-inputs": {
|
||||
"cli": "nx",
|
||||
"version": "15.0.0-beta.1",
|
||||
"description": "Replace implicitDependencies with namedInputs + target inputs",
|
||||
"implementation": "./src/migrations/update-15-0-0/migrate-to-inputs"
|
||||
},
|
||||
"15.0.0-prefix-outputs": {
|
||||
"cli": "nx",
|
||||
"version": "15.0.0-beta.1",
|
||||
"description": "Prefix outputs with {workspaceRoot}/{projectRoot} if needed",
|
||||
"implementation": "./src/migrations/update-15-0-0/prefix-outputs"
|
||||
},
|
||||
"15.1.0-set-project-names": {
|
||||
"cli": "nx",
|
||||
"version": "15.0.12-beta.1",
|
||||
"description": "Set project names in project.json files",
|
||||
"implementation": "./src/migrations/update-15-1-0/set-project-names"
|
||||
},
|
||||
"15.8.2-update-nx-wrapper": {
|
||||
"cli": "nx",
|
||||
"version": "15.8.2-beta.0",
|
||||
"description": "Updates the nx wrapper.",
|
||||
"implementation": "./src/migrations/update-15-8-2/update-nxw"
|
||||
},
|
||||
"16.0.0-remove-nrwl-cli": {
|
||||
"cli": "nx",
|
||||
"version": "16.0.0-beta.0",
|
||||
|
||||
@ -21,7 +21,6 @@ import { isCI } from '../../utils/is-ci';
|
||||
import { output } from '../../utils/output';
|
||||
import { handleErrors } from '../../utils/params';
|
||||
import { joinPathFragments } from '../../utils/path';
|
||||
import { getRootTsConfigPath } from '../../utils/typescript';
|
||||
import { workspaceRoot } from '../../utils/workspace-root';
|
||||
import { ChangelogOptions } from './command-object';
|
||||
import {
|
||||
@ -58,6 +57,7 @@ import {
|
||||
handleDuplicateGitTags,
|
||||
noDiffInChangelogMessage,
|
||||
} from './utils/shared';
|
||||
import { getRootTsConfigPath } from '../../plugins/js/utils/typescript';
|
||||
|
||||
export interface NxReleaseChangelogResult {
|
||||
workspaceChangelog?: {
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import { Argv, CommandModule, showHelp } from 'yargs';
|
||||
import { readNxJson } from '../../project-graph/file-utils';
|
||||
import { logger } from '../../utils/logger';
|
||||
import {
|
||||
OutputStyle,
|
||||
@ -10,6 +9,7 @@ import {
|
||||
withRunManyOptions,
|
||||
} from '../yargs-utils/shared-options';
|
||||
import { VersionData } from './utils/shared';
|
||||
import { readNxJson } from '../../config/nx-json';
|
||||
|
||||
export interface NxReleaseArgs {
|
||||
groups?: string[];
|
||||
|
||||
@ -43,6 +43,7 @@ export const patternsWeIgnoreInCommunityReport: Array<string | RegExp> = [
|
||||
];
|
||||
|
||||
const LINE_SEPARATOR = '---------------------------------------';
|
||||
|
||||
/**
|
||||
* Reports relevant version numbers for adding to an Nx issue report
|
||||
*
|
||||
@ -294,7 +295,7 @@ export function findRegisteredPluginsBeingUsed(nxJson: NxJsonConfiguration) {
|
||||
|
||||
export function findInstalledPackagesWeCareAbout() {
|
||||
const packagesWeMayCareAbout: Record<string, string> = {};
|
||||
// TODO (v19): Remove workaround for hiding @nrwl packages when matching @nx package is found.
|
||||
// TODO (v20): Remove workaround for hiding @nrwl packages when matching @nx package is found.
|
||||
const packageChangeMap: Record<string, string> = {
|
||||
'@nrwl/nx-plugin': '@nx/plugin',
|
||||
'@nx/plugin': '@nrwl/nx-plugin',
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { toProjectName } from './workspaces';
|
||||
import { toProjectName } from './to-project-name';
|
||||
import { TempFs } from '../internal-testing-utils/temp-fs';
|
||||
import { withEnvironmentVariables } from '../internal-testing-utils/with-environment';
|
||||
import { retrieveProjectConfigurations } from '../project-graph/utils/retrieve-workspace-files';
|
||||
10
packages/nx/src/config/to-project-name.ts
Normal file
10
packages/nx/src/config/to-project-name.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { dirname } from 'path';
|
||||
|
||||
/**
|
||||
* Pulled from toFileName in names from @nx/devkit.
|
||||
* Todo: Should refactor, not duplicate.
|
||||
*/
|
||||
export function toProjectName(fileName: string): string {
|
||||
const parts = dirname(fileName).split(/[\/\\]/g);
|
||||
return parts[parts.length - 1].toLowerCase();
|
||||
}
|
||||
@ -1,38 +0,0 @@
|
||||
import { dirname } from 'path';
|
||||
import {
|
||||
readCachedProjectGraph,
|
||||
readProjectsConfigurationFromProjectGraph,
|
||||
} from '../project-graph/project-graph';
|
||||
|
||||
import type { NxJsonConfiguration } from './nx-json';
|
||||
import { readNxJson } from './nx-json';
|
||||
import { ProjectsConfigurations } from './workspace-json-project-json';
|
||||
|
||||
// TODO(v19): remove this class
|
||||
/**
|
||||
* @deprecated This will be removed in v19. Use {@link readProjectsConfigurationFromProjectGraph} instead.
|
||||
*/
|
||||
export class Workspaces {
|
||||
constructor(private root: string) {}
|
||||
|
||||
/**
|
||||
* @deprecated Use {@link readProjectsConfigurationFromProjectGraph} instead.
|
||||
*/
|
||||
readWorkspaceConfiguration(): ProjectsConfigurations & NxJsonConfiguration {
|
||||
const nxJson = readNxJson(this.root);
|
||||
|
||||
return {
|
||||
...readProjectsConfigurationFromProjectGraph(readCachedProjectGraph()),
|
||||
...nxJson,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pulled from toFileName in names from @nx/devkit.
|
||||
* Todo: Should refactor, not duplicate.
|
||||
*/
|
||||
export function toProjectName(fileName: string): string {
|
||||
const parts = dirname(fileName).split(/[\/\\]/g);
|
||||
return parts[parts.length - 1].toLowerCase();
|
||||
}
|
||||
@ -37,12 +37,6 @@ export type {
|
||||
HasherContext,
|
||||
} from './config/misc-interfaces';
|
||||
|
||||
// TODO(v19): Remove this export
|
||||
/**
|
||||
* @category Workspace
|
||||
*/
|
||||
export { Workspaces } from './config/workspaces';
|
||||
|
||||
export { workspaceLayout } from './config/configuration';
|
||||
|
||||
export type {
|
||||
|
||||
@ -40,13 +40,13 @@ export interface Hash {
|
||||
|
||||
export interface TaskHasher {
|
||||
/**
|
||||
* @deprecated use hashTask(task:Task, taskGraph: TaskGraph, env: NodeJS.ProcessEnv) instead. This will be removed in v19
|
||||
* @deprecated use hashTask(task:Task, taskGraph: TaskGraph, env: NodeJS.ProcessEnv) instead. This will be removed in v20
|
||||
* @param task
|
||||
*/
|
||||
hashTask(task: Task): Promise<Hash>;
|
||||
|
||||
/**
|
||||
* @deprecated use hashTask(task:Task, taskGraph: TaskGraph, env: NodeJS.ProcessEnv) instead. This will be removed in v19
|
||||
* @deprecated use hashTask(task:Task, taskGraph: TaskGraph, env: NodeJS.ProcessEnv) instead. This will be removed in v20
|
||||
*/
|
||||
hashTask(task: Task, taskGraph: TaskGraph): Promise<Hash>;
|
||||
|
||||
@ -57,13 +57,13 @@ export interface TaskHasher {
|
||||
): Promise<Hash>;
|
||||
|
||||
/**
|
||||
* @deprecated use hashTasks(tasks:Task[], taskGraph: TaskGraph, env: NodeJS.ProcessEnv) instead. This will be removed in v19
|
||||
* @deprecated use hashTasks(tasks:Task[], taskGraph: TaskGraph, env: NodeJS.ProcessEnv) instead. This will be removed in v20
|
||||
* @param tasks
|
||||
*/
|
||||
hashTasks(tasks: Task[]): Promise<Hash[]>;
|
||||
|
||||
/**
|
||||
* @deprecated use hashTasks(tasks:Task[], taskGraph: TaskGraph, env: NodeJS.ProcessEnv) instead. This will be removed in v19
|
||||
* @deprecated use hashTasks(tasks:Task[], taskGraph: TaskGraph, env: NodeJS.ProcessEnv) instead. This will be removed in v20
|
||||
*/
|
||||
hashTasks(tasks: Task[], taskGraph: TaskGraph): Promise<Hash[]>;
|
||||
|
||||
|
||||
@ -1,38 +0,0 @@
|
||||
import { Tree } from '../../generators/tree';
|
||||
import { readNxJson } from '../../generators/utils/nx-json';
|
||||
import { dirname } from 'path';
|
||||
import { readJson, writeJson } from '../../generators/utils/json';
|
||||
import { formatChangedFilesWithPrettierIfAvailable } from '../../generators/internal-utils/format-changed-files-with-prettier-if-available';
|
||||
import { retrieveProjectConfigurationPaths } from '../../project-graph/utils/retrieve-workspace-files';
|
||||
import { loadNxPlugins } from '../../project-graph/plugins/internal-api';
|
||||
|
||||
export default async function (tree: Tree) {
|
||||
const nxJson = readNxJson(tree);
|
||||
const [plugins, cleanup] = await loadNxPlugins(
|
||||
nxJson?.plugins ?? [],
|
||||
tree.root
|
||||
);
|
||||
const projectFiles = retrieveProjectConfigurationPaths(tree.root, plugins);
|
||||
const projectJsons = projectFiles.filter((f) => f.endsWith('project.json'));
|
||||
|
||||
for (let f of projectJsons) {
|
||||
const projectJson = readJson(tree, f);
|
||||
if (!projectJson.name) {
|
||||
projectJson.name = toProjectName(dirname(f), nxJson);
|
||||
writeJson(tree, f, projectJson);
|
||||
}
|
||||
}
|
||||
await formatChangedFilesWithPrettierIfAvailable(tree);
|
||||
cleanup();
|
||||
}
|
||||
|
||||
function toProjectName(directory: string, nxJson: any): string {
|
||||
let { appsDir, libsDir } = nxJson?.workspaceLayout || {};
|
||||
appsDir ??= 'apps';
|
||||
libsDir ??= 'libs';
|
||||
const parts = directory.split(/[\/\\]/g);
|
||||
if ([appsDir, libsDir].includes(parts[0])) {
|
||||
parts.splice(0, 1);
|
||||
}
|
||||
return parts.join('-').toLowerCase();
|
||||
}
|
||||
@ -1,6 +0,0 @@
|
||||
import { Tree } from '../../generators/tree';
|
||||
import { updateNxw } from '../../utils/update-nxw';
|
||||
|
||||
export default async function (tree: Tree) {
|
||||
updateNxw(tree);
|
||||
}
|
||||
@ -4,7 +4,7 @@ import { dirname, join } from 'node:path';
|
||||
|
||||
import { NxJsonConfiguration, readNxJson } from '../../config/nx-json';
|
||||
import { ProjectConfiguration } from '../../config/workspace-json-project-json';
|
||||
import { toProjectName } from '../../config/workspaces';
|
||||
import { toProjectName } from '../../config/to-project-name';
|
||||
import { readJsonFile, readYamlFile } from '../../utils/fileutils';
|
||||
import { combineGlobPatterns } from '../../utils/globs';
|
||||
import { NX_PREFIX } from '../../utils/logger';
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { dirname, join } from 'node:path';
|
||||
|
||||
import { ProjectConfiguration } from '../../../config/workspace-json-project-json';
|
||||
import { toProjectName } from '../../../config/workspaces';
|
||||
import { toProjectName } from '../../../config/to-project-name';
|
||||
import { readJsonFile } from '../../../utils/fileutils';
|
||||
import { NxPluginV2 } from '../../../project-graph/plugins';
|
||||
import { CreateNodesError } from '../../../project-graph/error-types';
|
||||
|
||||
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