feat(misc): remove migrations for v14 and under (#19552)
This commit is contained in:
parent
89ad0b1dfb
commit
47e9fc8ed6
@ -315,7 +315,7 @@
|
||||
"@markdoc/markdoc": "0.2.2",
|
||||
"@monaco-editor/react": "^4.4.6",
|
||||
"@napi-rs/canvas": "^0.1.19",
|
||||
"@swc/helpers": "0.5.0",
|
||||
"@swc/helpers": "~0.5.2",
|
||||
"@tailwindcss/aspect-ratio": "^0.4.2",
|
||||
"@tailwindcss/forms": "^0.5.3",
|
||||
"@tailwindcss/line-clamp": "^0.4.2",
|
||||
|
||||
@ -1,23 +1,5 @@
|
||||
{
|
||||
"generators": {
|
||||
"remove-typescript-plugin": {
|
||||
"cli": "nx",
|
||||
"version": "12.8.0-beta.0",
|
||||
"description": "Remove Typescript Preprocessor Plugin",
|
||||
"factory": "./src/migrations/update-12-8-0/remove-typescript-plugin"
|
||||
},
|
||||
"update-cypress-configs-preset": {
|
||||
"cli": "nx",
|
||||
"version": "14.6.1-beta.0",
|
||||
"description": "Change Cypress e2e and component testing presets to use __filename instead of __dirname and include a devServerTarget for component testing.",
|
||||
"factory": "./src/migrations/update-14-6-1/update-cypress-configs-presets"
|
||||
},
|
||||
"update-cypress-if-v10": {
|
||||
"cli": "nx",
|
||||
"version": "14.7.0-beta.0",
|
||||
"description": "Update Cypress if using v10 to support latest component testing features",
|
||||
"factory": "./src/migrations/update-14-7-0/update-cypress-version-if-10"
|
||||
},
|
||||
"add-cypress-inputs": {
|
||||
"cli": "nx",
|
||||
"version": "15.0.0-beta.0",
|
||||
|
||||
@ -1,80 +0,0 @@
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { addProjectConfiguration, Tree, writeJson } from '@nx/devkit';
|
||||
import removeTypescriptPlugin from './remove-typescript-plugin';
|
||||
|
||||
describe('remove typescript plugin', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
|
||||
addProjectConfiguration(tree, 'proj', {
|
||||
root: 'proj',
|
||||
targets: {
|
||||
cypress: {
|
||||
executor: '@nrwl/cypress:cypress',
|
||||
options: {
|
||||
cypressConfig: 'proj/cypress.json',
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
writeJson(tree, 'proj/cypress.json', {
|
||||
pluginsFile: './plugins.js',
|
||||
});
|
||||
|
||||
tree.write(
|
||||
'proj/plugins.js',
|
||||
`
|
||||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
// You can change the location of this file or turn off loading
|
||||
// the plugins file with the 'pluginsFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/plugins-guide
|
||||
// ***********************************************************
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// the project's config changing)
|
||||
|
||||
const { preprocessTypescript } = require('@nx/cypress/plugins/preprocessor');
|
||||
|
||||
module.exports = (on, config) => {
|
||||
// \`on\` is used to hook into various events Cypress emits
|
||||
// \`config\` is the resolved Cypress config
|
||||
|
||||
// Preprocess Typescript
|
||||
on('file:preprocessor', preprocessTypescript(config));
|
||||
};
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
it('should remove the plugin', async () => {
|
||||
await removeTypescriptPlugin(tree);
|
||||
|
||||
expect(tree.read('proj/plugins.js', 'utf-8')).not.toContain(
|
||||
'preprocessTypescript(config)'
|
||||
);
|
||||
});
|
||||
|
||||
it('should not remove the plugin if they have a custom webpack config', async () => {
|
||||
tree.write(
|
||||
'proj/plugins.js',
|
||||
tree
|
||||
.read('proj/plugins.js', 'utf-8')
|
||||
.replace(
|
||||
'preprocessTypescript(config)',
|
||||
'preprocessTypescript(config, webpackFunction)'
|
||||
)
|
||||
);
|
||||
await removeTypescriptPlugin(tree);
|
||||
|
||||
expect(tree.read('proj/plugins.js', 'utf-8')).toContain(
|
||||
'preprocessTypescript(config, webpackFunction)'
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -1,125 +0,0 @@
|
||||
import {
|
||||
applyChangesToString,
|
||||
ChangeType,
|
||||
formatFiles,
|
||||
getProjects,
|
||||
ProjectConfiguration,
|
||||
readJson,
|
||||
StringDeletion,
|
||||
TargetConfiguration,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
import {
|
||||
createSourceFile,
|
||||
isCallExpression,
|
||||
isExpressionStatement,
|
||||
isIdentifier,
|
||||
Node,
|
||||
ScriptTarget,
|
||||
} from 'typescript';
|
||||
import { dirname, join } from 'path';
|
||||
import { installedCypressVersion } from '../../utils/cypress-version';
|
||||
|
||||
export default async function removeTypescriptPlugin(tree: Tree) {
|
||||
const cypressVersion = installedCypressVersion();
|
||||
if (cypressVersion < 7) {
|
||||
console.warn(
|
||||
`Cypress v${cypressVersion} is installed. This migration was skipped. Please rerun this migration after updating to Cypress 7.`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const [_, proj] of getProjects(tree)) {
|
||||
const cypressTargets = getCypressTargets(proj);
|
||||
if (cypressTargets.length <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const target of cypressTargets) {
|
||||
const cypressConfigs = getCypressConfigs(target);
|
||||
|
||||
for (const config of cypressConfigs) {
|
||||
const cypressConfig = readJson(tree, config);
|
||||
if (cypressConfig.pluginsFile) {
|
||||
let pluginPath = join(dirname(config), cypressConfig.pluginsFile);
|
||||
if (!tree.exists(pluginPath)) {
|
||||
pluginPath = ['.js', '.ts']
|
||||
.map((ext) => pluginPath + ext)
|
||||
.find((path) => tree.exists(path));
|
||||
}
|
||||
removePreprocessor(tree, pluginPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
function removePreprocessor(tree: Tree, pluginPath: string) {
|
||||
const pluginContents = tree.read(pluginPath, 'utf-8');
|
||||
const sourceFile = createSourceFile(
|
||||
pluginPath,
|
||||
pluginContents,
|
||||
ScriptTarget.ESNext,
|
||||
true
|
||||
);
|
||||
|
||||
const deletions: StringDeletion[] = [];
|
||||
|
||||
const callback = (node: Node) => {
|
||||
// Look for the invocation of preprocessTypescript
|
||||
if (
|
||||
isCallExpression(node) &&
|
||||
isIdentifier(node.expression) &&
|
||||
node.expression.getText(sourceFile) === 'preprocessTypescript' &&
|
||||
node.arguments.length < 2
|
||||
) {
|
||||
// Get the Statement that the function call belongs to
|
||||
let n: Node = node.parent;
|
||||
while (!isExpressionStatement(n) && n === sourceFile) {
|
||||
n = n.parent;
|
||||
}
|
||||
|
||||
deletions.push({
|
||||
type: ChangeType.Delete,
|
||||
start: n.getStart(),
|
||||
length:
|
||||
n.getWidth() +
|
||||
(pluginContents[n.getStart() + n.getWidth()] === ';' ? 1 : 0),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Call the callback for every node in the file
|
||||
sourceFile.forEachChild(recurse);
|
||||
|
||||
function recurse(node: Node) {
|
||||
callback(node);
|
||||
node.forEachChild((child) => recurse(child));
|
||||
}
|
||||
|
||||
// Remove the preprocessor from the file
|
||||
tree.write(pluginPath, applyChangesToString(pluginContents, deletions));
|
||||
}
|
||||
|
||||
function getCypressConfigs(target: TargetConfiguration): string[] {
|
||||
if (!target.configurations && !target.options.cypressConfig) {
|
||||
return [];
|
||||
} else if (!target.configurations && target.options.cypressConfig) {
|
||||
return [target.options.cypressConfig];
|
||||
}
|
||||
|
||||
return [target.options, Object.values(target.configurations)]
|
||||
.filter((options) => !!options.cypressConfig)
|
||||
.map((options) => options.cypressConfig);
|
||||
}
|
||||
|
||||
function getCypressTargets(proj: ProjectConfiguration) {
|
||||
if (!proj.targets) {
|
||||
return [];
|
||||
}
|
||||
return Object.values(proj.targets).filter(
|
||||
(target) => target.executor === '@nrwl/cypress:cypress'
|
||||
);
|
||||
}
|
||||
@ -1,420 +0,0 @@
|
||||
import { updateCypressConfigsPresets } from './update-cypress-configs-presets';
|
||||
import { installedCypressVersion } from '../../utils/cypress-version';
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
DependencyType,
|
||||
logger,
|
||||
ProjectGraph,
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { cypressProjectGenerator } from '../../generators/cypress-project/cypress-project';
|
||||
import { libraryGenerator } from '@nx/js';
|
||||
|
||||
let projectGraph: ProjectGraph;
|
||||
jest.mock('@nx/devkit', () => {
|
||||
return {
|
||||
...jest.requireActual('@nx/devkit'),
|
||||
createProjectGraphAsync: jest.fn().mockImplementation(() => projectGraph),
|
||||
readTargetOptions: jest.fn().mockImplementation(() => ({})),
|
||||
};
|
||||
});
|
||||
jest.mock('../../utils/cypress-version');
|
||||
describe('updateComponentTestingConfig', () => {
|
||||
let tree: Tree;
|
||||
let mockedInstalledCypressVersion: jest.Mock<
|
||||
ReturnType<typeof installedCypressVersion>
|
||||
> = installedCypressVersion as never;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
});
|
||||
it('should update', async () => {
|
||||
mockedInstalledCypressVersion.mockReturnValue(10);
|
||||
await setup(tree, { name: 'something' });
|
||||
await updateCypressConfigsPresets(tree);
|
||||
expect(
|
||||
tree.read('libs/something-lib/cypress.config.ts', 'utf-8')
|
||||
).toContain(
|
||||
`export default defineConfig({
|
||||
component: nxComponentTestingPreset(__filename),
|
||||
});
|
||||
`
|
||||
);
|
||||
expect(
|
||||
tree.read('libs/something-lib/cypress.config-two.ts', 'utf-8')
|
||||
).toContain(
|
||||
`export default defineConfig({
|
||||
component: nxComponentTestingPreset(__filename, { ctTargetName: 'ct' }),
|
||||
});
|
||||
`
|
||||
);
|
||||
|
||||
expect(tree.read('apps/something-e2e/cypress.config.ts', 'utf-8'))
|
||||
.toContain(`export default defineConfig({
|
||||
e2e: nxE2EPreset(__filename),
|
||||
});
|
||||
`);
|
||||
expect(tree.read('apps/something-e2e/cypress.storybook-config.ts', 'utf-8'))
|
||||
.toContain(`export default defineConfig({
|
||||
e2e: nxE2EStorybookPreset(__filename),
|
||||
});
|
||||
`);
|
||||
const libProjectConfig = readProjectConfiguration(tree, 'something-lib');
|
||||
expect(libProjectConfig.targets['component-test']).toEqual({
|
||||
executor: '@nrwl/cypress:cypress',
|
||||
options: {
|
||||
cypressConfig: 'libs/something-lib/cypress.config.ts',
|
||||
testingType: 'component',
|
||||
devServerTarget: 'something-app:build',
|
||||
skipServe: true,
|
||||
},
|
||||
});
|
||||
expect(libProjectConfig.targets['ct']).toEqual({
|
||||
executor: '@nrwl/cypress:cypress',
|
||||
options: {
|
||||
cypressConfig: 'libs/something-lib/cypress.config-two.ts',
|
||||
testingType: 'component',
|
||||
devServerTarget: 'something-app:build',
|
||||
skipServe: true,
|
||||
},
|
||||
configurations: {
|
||||
prod: {
|
||||
baseUrl: 'https://example.com',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should list out projects when unable to update config', async () => {
|
||||
const loggerSpy = jest.spyOn(logger, 'warn');
|
||||
await setup(tree, { name: 'something' });
|
||||
projectGraph = {
|
||||
nodes: {},
|
||||
dependencies: {},
|
||||
};
|
||||
await updateCypressConfigsPresets(tree);
|
||||
|
||||
expect(loggerSpy.mock.calls).toEqual([
|
||||
[
|
||||
'Unable to find a build target to add to the component testing target in the following projects:',
|
||||
],
|
||||
['- something-lib'],
|
||||
[
|
||||
`You can manually add the 'devServerTarget' option to the
|
||||
component testing target to specify the build target to use.
|
||||
The build configuration should be using @nrwl/web:webpack as the executor.
|
||||
Usually this is a React app in your workspace.
|
||||
Component testing will fallback to a default configuration if one isn't provided,
|
||||
but might require modifications if your projects are more complex.`,
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle already updated config', async () => {
|
||||
mockedInstalledCypressVersion.mockReturnValue(10);
|
||||
await setup(tree, { name: 'something' });
|
||||
|
||||
expect(async () => {
|
||||
await updateCypressConfigsPresets(tree);
|
||||
}).not.toThrow();
|
||||
expect(tree.read('libs/something-lib/cypress.config.ts', 'utf-8'))
|
||||
.toContain(`export default defineConfig({
|
||||
component: nxComponentTestingPreset(__filename),
|
||||
});
|
||||
`);
|
||||
expect(
|
||||
tree.read('libs/something-lib/cypress.config-two.ts', 'utf-8')
|
||||
).toContain(
|
||||
`export default defineConfig({
|
||||
component: nxComponentTestingPreset(__filename, { ctTargetName: 'ct' }),
|
||||
});
|
||||
`
|
||||
);
|
||||
|
||||
expect(tree.read('apps/something-e2e/cypress.config.ts', 'utf-8'))
|
||||
.toContain(`export default defineConfig({
|
||||
e2e: nxE2EPreset(__filename),
|
||||
});
|
||||
`);
|
||||
});
|
||||
it('should not update if using < v10', async () => {
|
||||
mockedInstalledCypressVersion.mockReturnValue(9);
|
||||
await setup(tree, { name: 'something' });
|
||||
await updateCypressConfigsPresets(tree);
|
||||
expect(
|
||||
tree.read('libs/something-lib/cypress.config.ts', 'utf-8')
|
||||
).toContain(
|
||||
`export default defineConfig({
|
||||
component: nxComponentTestingPreset(__dirname),
|
||||
});
|
||||
`
|
||||
);
|
||||
expect(
|
||||
tree.read('libs/something-lib/cypress.config-two.ts', 'utf-8')
|
||||
).toContain(
|
||||
`export default defineConfig({
|
||||
component: nxComponentTestingPreset(__dirname),
|
||||
});
|
||||
`
|
||||
);
|
||||
|
||||
expect(tree.read('apps/something-e2e/cypress.config.ts', 'utf-8'))
|
||||
.toContain(`export default defineConfig({
|
||||
e2e: nxE2EPreset(__dirname),
|
||||
});
|
||||
`);
|
||||
});
|
||||
|
||||
it('should be idempotent', async () => {
|
||||
mockedInstalledCypressVersion.mockReturnValue(10);
|
||||
await setup(tree, { name: 'something' });
|
||||
await updateCypressConfigsPresets(tree);
|
||||
expect(
|
||||
tree.read('libs/something-lib/cypress.config.ts', 'utf-8')
|
||||
).toContain(
|
||||
`export default defineConfig({
|
||||
component: nxComponentTestingPreset(__filename),
|
||||
});
|
||||
`
|
||||
);
|
||||
expect(
|
||||
tree.read('libs/something-lib/cypress.config-two.ts', 'utf-8')
|
||||
).toContain(
|
||||
`export default defineConfig({
|
||||
component: nxComponentTestingPreset(__filename, { ctTargetName: 'ct' }),
|
||||
});
|
||||
`
|
||||
);
|
||||
|
||||
expect(tree.read('apps/something-e2e/cypress.config.ts', 'utf-8'))
|
||||
.toContain(`export default defineConfig({
|
||||
e2e: nxE2EPreset(__filename),
|
||||
});
|
||||
`);
|
||||
const libProjectConfig = readProjectConfiguration(tree, 'something-lib');
|
||||
expect(libProjectConfig.targets['component-test']).toEqual({
|
||||
executor: '@nrwl/cypress:cypress',
|
||||
options: {
|
||||
cypressConfig: 'libs/something-lib/cypress.config.ts',
|
||||
testingType: 'component',
|
||||
devServerTarget: 'something-app:build',
|
||||
skipServe: true,
|
||||
},
|
||||
});
|
||||
expect(libProjectConfig.targets['ct']).toEqual({
|
||||
executor: '@nrwl/cypress:cypress',
|
||||
options: {
|
||||
cypressConfig: 'libs/something-lib/cypress.config-two.ts',
|
||||
testingType: 'component',
|
||||
devServerTarget: 'something-app:build',
|
||||
skipServe: true,
|
||||
},
|
||||
configurations: {
|
||||
prod: {
|
||||
baseUrl: 'https://example.com',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await updateCypressConfigsPresets(tree);
|
||||
expect(
|
||||
tree.read('libs/something-lib/cypress.config.ts', 'utf-8')
|
||||
).toContain(
|
||||
`export default defineConfig({
|
||||
component: nxComponentTestingPreset(__filename),
|
||||
});
|
||||
`
|
||||
);
|
||||
expect(
|
||||
tree.read('libs/something-lib/cypress.config-two.ts', 'utf-8')
|
||||
).toContain(
|
||||
`export default defineConfig({
|
||||
component: nxComponentTestingPreset(__filename, { ctTargetName: 'ct' }),
|
||||
});
|
||||
`
|
||||
);
|
||||
|
||||
expect(tree.read('apps/something-e2e/cypress.config.ts', 'utf-8'))
|
||||
.toContain(`export default defineConfig({
|
||||
e2e: nxE2EPreset(__filename),
|
||||
});
|
||||
`);
|
||||
const libProjectConfig2 = readProjectConfiguration(tree, 'something-lib');
|
||||
expect(libProjectConfig2.targets['component-test']).toEqual({
|
||||
executor: '@nrwl/cypress:cypress',
|
||||
options: {
|
||||
cypressConfig: 'libs/something-lib/cypress.config.ts',
|
||||
testingType: 'component',
|
||||
devServerTarget: 'something-app:build',
|
||||
skipServe: true,
|
||||
},
|
||||
});
|
||||
expect(libProjectConfig2.targets['ct']).toEqual({
|
||||
executor: '@nrwl/cypress:cypress',
|
||||
options: {
|
||||
cypressConfig: 'libs/something-lib/cypress.config-two.ts',
|
||||
testingType: 'component',
|
||||
devServerTarget: 'something-app:build',
|
||||
skipServe: true,
|
||||
},
|
||||
configurations: {
|
||||
prod: {
|
||||
baseUrl: 'https://example.com',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
async function setup(tree: Tree, options: { name: string }) {
|
||||
const appName = `${options.name}-app`;
|
||||
const libName = `${options.name}-lib`;
|
||||
const e2eName = `${options.name}-e2e`;
|
||||
tree.write(
|
||||
'apps/my-app/cypress.config.ts',
|
||||
`import { defineConfig } from 'cypress';
|
||||
import { nxComponentTestingPreset } from '@nrwl/cypress/plugins/component-testing';
|
||||
|
||||
export default defineConfig({
|
||||
component: nxComponentTestingPreset(__dirname),
|
||||
});
|
||||
`
|
||||
);
|
||||
|
||||
addProjectConfiguration(tree, appName, {
|
||||
root: `apps/my-app`,
|
||||
sourceRoot: `apps/${appName}/src`,
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/web:webpack',
|
||||
outputs: ['{options.outputPath}'],
|
||||
options: {
|
||||
compiler: 'babel',
|
||||
outputPath: `dist/apps/${appName}`,
|
||||
index: `apps/${appName}/src/index.html`,
|
||||
baseHref: '/',
|
||||
main: `apps/${appName}/src/main.tsx`,
|
||||
polyfills: `apps/${appName}/src/polyfills.ts`,
|
||||
tsConfig: `apps/${appName}/tsconfig.app.json`,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
await cypressProjectGenerator(tree, { project: appName, name: e2eName });
|
||||
const e2eProjectConfig = readProjectConfiguration(tree, e2eName);
|
||||
e2eProjectConfig.targets['e2e'] = {
|
||||
...e2eProjectConfig.targets['e2e'],
|
||||
executor: '@nrwl/cypress:cypress',
|
||||
};
|
||||
e2eProjectConfig.targets['e2e'].configurations = {
|
||||
...e2eProjectConfig.targets['e2e'].configurations,
|
||||
sb: {
|
||||
cypressConfig: `apps/${e2eName}/cypress.storybook-config.ts`,
|
||||
},
|
||||
};
|
||||
updateProjectConfiguration(tree, e2eName, e2eProjectConfig);
|
||||
tree.write(
|
||||
`apps/${e2eName}/cypress.config.ts`,
|
||||
`import { defineConfig } from 'cypress';
|
||||
import { nxE2EPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||
|
||||
export default defineConfig({
|
||||
e2e: nxE2EPreset(__dirname),
|
||||
});
|
||||
`
|
||||
);
|
||||
tree.write(
|
||||
`apps/${e2eName}/cypress.storybook-config.ts`,
|
||||
`
|
||||
import { defineConfig } from 'cypress';
|
||||
import { nxE2EStorybookPreset } from '@nrwl/cypress/plugins/cypress-preset';
|
||||
|
||||
export default defineConfig({
|
||||
e2e: nxE2EStorybookPreset(__dirname),
|
||||
});
|
||||
`
|
||||
);
|
||||
// lib
|
||||
await libraryGenerator(tree, { name: libName });
|
||||
const libProjectConfig = readProjectConfiguration(tree, libName);
|
||||
libProjectConfig.targets = {
|
||||
...libProjectConfig.targets,
|
||||
'component-test': {
|
||||
executor: '@nrwl/cypress:cypress',
|
||||
options: {
|
||||
testingType: 'component',
|
||||
cypressConfig: `libs/${libName}/cypress.config.ts`,
|
||||
},
|
||||
},
|
||||
ct: {
|
||||
executor: '@nrwl/cypress:cypress',
|
||||
options: {
|
||||
testingType: 'component',
|
||||
cypressConfig: `libs/${libName}/cypress.config-two.ts`,
|
||||
},
|
||||
configurations: {
|
||||
prod: {
|
||||
baseUrl: 'https://example.com',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
updateProjectConfiguration(tree, libName, libProjectConfig);
|
||||
tree.write(
|
||||
`libs/${libName}/cypress.config.ts`,
|
||||
`import { defineConfig } from 'cypress';
|
||||
import { nxComponentTestingPreset } from '@nrwl/cypress/plugins/component-testing';
|
||||
|
||||
export default defineConfig({
|
||||
component: nxComponentTestingPreset(__dirname),
|
||||
});
|
||||
`
|
||||
);
|
||||
tree.write(
|
||||
`libs/${libName}/cypress.config-two.ts`,
|
||||
`import { defineConfig } from 'cypress';
|
||||
import { nxComponentTestingPreset } from '@nrwl/cypress/plugins/component-testing';
|
||||
|
||||
export default defineConfig({
|
||||
component: nxComponentTestingPreset(__dirname),
|
||||
});
|
||||
`
|
||||
);
|
||||
|
||||
projectGraph = {
|
||||
nodes: {
|
||||
[appName]: {
|
||||
name: appName,
|
||||
type: 'app',
|
||||
data: {
|
||||
...readProjectConfiguration(tree, appName),
|
||||
},
|
||||
},
|
||||
[e2eName]: {
|
||||
name: e2eName,
|
||||
type: 'e2e',
|
||||
data: {
|
||||
...readProjectConfiguration(tree, e2eName),
|
||||
},
|
||||
},
|
||||
[libName]: {
|
||||
name: libName,
|
||||
type: 'lib',
|
||||
data: {
|
||||
...readProjectConfiguration(tree, libName),
|
||||
},
|
||||
},
|
||||
} as any,
|
||||
dependencies: {
|
||||
[appName]: [
|
||||
{ type: DependencyType.static, source: appName, target: libName },
|
||||
],
|
||||
[e2eName]: [
|
||||
{ type: DependencyType.implicit, source: e2eName, target: libName },
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
@ -1,170 +0,0 @@
|
||||
import {
|
||||
logger,
|
||||
readProjectConfiguration,
|
||||
stripIndents,
|
||||
Tree,
|
||||
updateJson,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { tsquery } from '@phenomnomnominal/tsquery';
|
||||
import * as ts from 'typescript';
|
||||
import { CypressExecutorOptions } from '../../executors/cypress/cypress.impl';
|
||||
import { installedCypressVersion } from '../../utils/cypress-version';
|
||||
import { findBuildConfig } from '../../utils/find-target-options';
|
||||
|
||||
export async function updateCypressConfigsPresets(tree: Tree) {
|
||||
if (installedCypressVersion() < 10) {
|
||||
return;
|
||||
}
|
||||
|
||||
const projectsWithoutDevServerTarget = new Set<string>();
|
||||
const updateTasks = [];
|
||||
forEachExecutorOptions<CypressExecutorOptions>(
|
||||
tree,
|
||||
'@nrwl/cypress:cypress',
|
||||
(options, projectName, targetName, configName) => {
|
||||
if (options.cypressConfig && tree.exists(options.cypressConfig)) {
|
||||
updatePreset(tree, options, targetName);
|
||||
}
|
||||
|
||||
const projectConfig = readProjectConfiguration(tree, projectName);
|
||||
const testingType =
|
||||
options.testingType ||
|
||||
projectConfig.targets[targetName]?.options?.testingType;
|
||||
const devServerTarget =
|
||||
options.devServerTarget ||
|
||||
projectConfig.targets[targetName]?.options?.devServerTarget;
|
||||
|
||||
if (!devServerTarget && testingType === 'component') {
|
||||
updateTasks.push(
|
||||
addBuildTargetToConfig(
|
||||
tree,
|
||||
projectName,
|
||||
targetName,
|
||||
configName
|
||||
).then((didUpdate) => {
|
||||
if (!didUpdate) {
|
||||
projectsWithoutDevServerTarget.add(projectName);
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if (updateTasks.length > 0) {
|
||||
cacheComponentTestTarget(tree);
|
||||
}
|
||||
|
||||
await Promise.all(updateTasks);
|
||||
|
||||
if (projectsWithoutDevServerTarget.size > 0) {
|
||||
logger.warn(
|
||||
`Unable to find a build target to add to the component testing target in the following projects:`
|
||||
);
|
||||
logger.warn(`- ${Array.from(projectsWithoutDevServerTarget).join('\n- ')}`);
|
||||
logger.warn(stripIndents`
|
||||
You can manually add the 'devServerTarget' option to the
|
||||
component testing target to specify the build target to use.
|
||||
The build configuration should be using @nrwl/web:webpack as the executor.
|
||||
Usually this is a React app in your workspace.
|
||||
Component testing will fallback to a default configuration if one isn't provided,
|
||||
but might require modifications if your projects are more complex.
|
||||
`);
|
||||
}
|
||||
}
|
||||
|
||||
function updatePreset(
|
||||
tree: Tree,
|
||||
options: CypressExecutorOptions,
|
||||
targetName: string | undefined
|
||||
) {
|
||||
let contents = tsquery.replace(
|
||||
tree.read(options.cypressConfig, 'utf-8'),
|
||||
'CallExpression',
|
||||
(node: ts.CallExpression) => {
|
||||
// technically someone could have both component and e2e in the same project.
|
||||
const expression = node.expression.getText();
|
||||
if (expression === 'nxE2EPreset') {
|
||||
return 'nxE2EPreset(__filename)';
|
||||
} else if (expression === 'nxE2EStorybookPreset') {
|
||||
return 'nxE2EStorybookPreset(__filename)';
|
||||
} else if (node.expression.getText() === 'nxComponentTestingPreset') {
|
||||
return targetName && targetName !== 'component-test' // the default
|
||||
? `nxComponentTestingPreset(__filename, { ctTargetName: '${targetName}' })`
|
||||
: 'nxComponentTestingPreset(__filename)';
|
||||
}
|
||||
return;
|
||||
}
|
||||
);
|
||||
|
||||
tree.write(options.cypressConfig, contents);
|
||||
}
|
||||
|
||||
async function addBuildTargetToConfig(
|
||||
tree: Tree,
|
||||
projectName: string,
|
||||
targetName: string,
|
||||
configName?: string
|
||||
): Promise<boolean> {
|
||||
const projectWithBuild = await findBuildConfig(tree, {
|
||||
project: projectName,
|
||||
validExecutorNames: new Set(['@nrwl/web:webpack']),
|
||||
skipGetOptions: true,
|
||||
});
|
||||
// didn't find the config so can't update. consumer should collect list of them and display a warning at the end
|
||||
// no reason to fail since the preset will fallback to a default config so should still keep working.
|
||||
if (!projectWithBuild?.target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const projectConfig = readProjectConfiguration(tree, projectName);
|
||||
// if using a custom config and the devServerTarget default args
|
||||
// has a different target, then add it to the custom target config
|
||||
// otherwise add it to the default options
|
||||
if (
|
||||
configName &&
|
||||
projectWithBuild.target !==
|
||||
projectConfig.targets[targetName]?.options?.devServerTarget
|
||||
) {
|
||||
projectConfig.targets[targetName].configurations[configName] = {
|
||||
...projectConfig.targets[targetName].configurations[configName],
|
||||
devServerTarget: projectWithBuild.target,
|
||||
skipServe: true,
|
||||
};
|
||||
} else {
|
||||
projectConfig.targets[targetName].options = {
|
||||
...projectConfig.targets[targetName].options,
|
||||
devServerTarget: projectWithBuild.target,
|
||||
skipServe: true,
|
||||
};
|
||||
}
|
||||
|
||||
updateProjectConfiguration(tree, projectName, projectConfig);
|
||||
return true;
|
||||
}
|
||||
|
||||
function cacheComponentTestTarget(tree: Tree) {
|
||||
updateJson(tree, 'nx.json', (json) => ({
|
||||
...json,
|
||||
tasksRunnerOptions: {
|
||||
...json.tasksRunnerOptions,
|
||||
default: {
|
||||
...json.tasksRunnerOptions?.default,
|
||||
options: {
|
||||
...json.tasksRunnerOptions?.default?.options,
|
||||
cacheableOperations: Array.from(
|
||||
new Set([
|
||||
...(json.tasksRunnerOptions?.default?.options
|
||||
?.cacheableOperations ?? []),
|
||||
'component-test',
|
||||
])
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
export default updateCypressConfigsPresets;
|
||||
@ -1,78 +0,0 @@
|
||||
import { updateCypressVersionIf10 } from './update-cypress-version-if-10';
|
||||
import { installedCypressVersion } from '../../utils/cypress-version';
|
||||
import { readJson, Tree, updateJson } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
|
||||
describe('Update Cypress if v10 migration', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
tree.write(
|
||||
'package.json',
|
||||
JSON.stringify({
|
||||
dependencies: {},
|
||||
devDependencies: {},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should update the version if the installed version is v10', () => {
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
json.devDependencies['cypress'] = '^10.5.0';
|
||||
return json;
|
||||
});
|
||||
updateCypressVersionIf10(tree);
|
||||
const pkgJson = readJson(tree, 'package.json');
|
||||
expect(pkgJson.devDependencies['cypress']).toBe('^10.7.0');
|
||||
});
|
||||
|
||||
it('should not update the version < v10', () => {
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
json.devDependencies['cypress'] = '9.0.0';
|
||||
return json;
|
||||
});
|
||||
updateCypressVersionIf10(tree);
|
||||
const pkgJson = readJson(tree, 'package.json');
|
||||
expect(pkgJson.devDependencies['cypress']).toBe('9.0.0');
|
||||
});
|
||||
|
||||
it('should not update if the version > v10', () => {
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
json.devDependencies['cypress'] = '11.0.0';
|
||||
return json;
|
||||
});
|
||||
updateCypressVersionIf10(tree);
|
||||
const pkgJson = readJson(tree, 'package.json');
|
||||
expect(pkgJson.devDependencies['cypress']).toBe('11.0.0');
|
||||
});
|
||||
|
||||
it('should not update if the version is not defined', () => {
|
||||
updateCypressVersionIf10(tree);
|
||||
const pkgJson = readJson(tree, 'package.json');
|
||||
expect(pkgJson.devDependencies['cypress']).toBe(undefined);
|
||||
});
|
||||
|
||||
it('should not update if v10.7.0 < version < v11', () => {
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
json.devDependencies['cypress'] = '^10.8.0';
|
||||
return json;
|
||||
});
|
||||
updateCypressVersionIf10(tree);
|
||||
const pkgJson1 = readJson(tree, 'package.json');
|
||||
expect(pkgJson1.devDependencies['cypress']).toBe('^10.8.0');
|
||||
});
|
||||
|
||||
it('should be idempotent', () => {
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
json.devDependencies['cypress'] = '^10.3.0';
|
||||
return json;
|
||||
});
|
||||
updateCypressVersionIf10(tree);
|
||||
const pkgJson1 = readJson(tree, 'package.json');
|
||||
expect(pkgJson1.devDependencies['cypress']).toBe('^10.7.0');
|
||||
updateCypressVersionIf10(tree);
|
||||
const pkgJson2 = readJson(tree, 'package.json');
|
||||
expect(pkgJson2.devDependencies['cypress']).toBe('^10.7.0');
|
||||
});
|
||||
});
|
||||
@ -1,46 +0,0 @@
|
||||
import {
|
||||
GeneratorCallback,
|
||||
installPackagesTask,
|
||||
readJson,
|
||||
Tree,
|
||||
updateJson,
|
||||
} from '@nx/devkit';
|
||||
// don't import from root level to prevent issue where angular isn't installed.
|
||||
import { checkAndCleanWithSemver } from '@nx/devkit/src/utils/semver';
|
||||
import { gte, lt } from 'semver';
|
||||
|
||||
export function updateCypressVersionIf10(tree: Tree): GeneratorCallback {
|
||||
const installedVersion = readJson(tree, 'package.json').devDependencies?.[
|
||||
'cypress'
|
||||
];
|
||||
|
||||
if (!installedVersion) {
|
||||
return;
|
||||
}
|
||||
const normalizedInstalledCypressVersion = checkAndCleanWithSemver(
|
||||
'cypress',
|
||||
installedVersion
|
||||
);
|
||||
|
||||
// not using v10
|
||||
if (
|
||||
lt(normalizedInstalledCypressVersion, '10.0.0') ||
|
||||
gte(normalizedInstalledCypressVersion, '11.0.0')
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const ngComponentTestingVersion = '10.7.0';
|
||||
|
||||
if (lt(normalizedInstalledCypressVersion, ngComponentTestingVersion)) {
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
json.devDependencies['cypress'] = `^${ngComponentTestingVersion}`;
|
||||
return json;
|
||||
});
|
||||
return () => {
|
||||
installPackagesTask(tree);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default updateCypressVersionIf10;
|
||||
@ -1,23 +1,5 @@
|
||||
{
|
||||
"generators": {
|
||||
"add-build-target-test-13-5-0": {
|
||||
"version": "13.5.0-beta.0",
|
||||
"cli": "nx",
|
||||
"description": "add buildTarget to test-ios and test-android for detox app",
|
||||
"factory": "./src/migrations/update-13-5-0/add-build-target-test-13-5-0"
|
||||
},
|
||||
"remove-types-detox-13-8-2": {
|
||||
"version": "13.8.2-beta.0",
|
||||
"cli": "nx",
|
||||
"description": "remove deprecated @types/detox from package.json",
|
||||
"factory": "./src/migrations/update-13-8-2/remove-types-detox-13-8-2"
|
||||
},
|
||||
"add-verbose-jest-config-13-10-3": {
|
||||
"version": "13.10.3-beta.0",
|
||||
"cli": "nx",
|
||||
"description": "Update jest.config.json under detox project, add key verbsoe: true",
|
||||
"factory": "./src/migrations/update-13-10-3/add-verbose-jest-config-13-10-3"
|
||||
},
|
||||
"update-16-0-0-add-nx-packages": {
|
||||
"cli": "nx",
|
||||
"version": "16.0.0-beta.1",
|
||||
@ -38,152 +20,6 @@
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
"12.8.0": {
|
||||
"version": "12.8.0-beta.0",
|
||||
"packages": {
|
||||
"detox": {
|
||||
"version": "18.20.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"12.10.0-beta.1": {
|
||||
"version": "12.10.0-beta.1",
|
||||
"packages": {
|
||||
"detox": {
|
||||
"version": "18.22.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"13.0.0": {
|
||||
"version": "13.0.0-beta.1",
|
||||
"packages": {
|
||||
"detox": {
|
||||
"version": "18.22.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"13.2.0": {
|
||||
"version": "13.2.0-beta.0",
|
||||
"packages": {
|
||||
"detox": {
|
||||
"version": "19.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@testing-library/jest-dom": {
|
||||
"version": "5.15.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"13.5.0": {
|
||||
"version": "13.5.0-beta.0",
|
||||
"packages": {
|
||||
"detox": {
|
||||
"version": "19.4.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@testing-library/jest-dom": {
|
||||
"version": "5.16.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"13.8.2": {
|
||||
"version": "13.8.2-beta.0",
|
||||
"packages": {
|
||||
"detox": {
|
||||
"version": "19.4.5",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@testing-library/jest-dom": {
|
||||
"version": "5.16.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"13.8.6": {
|
||||
"version": "13.8.6-beta.0",
|
||||
"packages": {
|
||||
"detox": {
|
||||
"version": "19.5.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"13.9.3": {
|
||||
"version": "13.9.3-beta.0",
|
||||
"packages": {
|
||||
"detox": {
|
||||
"version": "19.5.7",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"13.10.3": {
|
||||
"version": "13.10.3-beta.0",
|
||||
"packages": {
|
||||
"detox": {
|
||||
"version": "19.6.5",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@testing-library/jest-dom": {
|
||||
"version": "5.16.4",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.1.8": {
|
||||
"version": "14.1.8-beta.0",
|
||||
"packages": {
|
||||
"detox": {
|
||||
"version": "19.6.9",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.2.1": {
|
||||
"version": "14.2.1-beta.0",
|
||||
"packages": {
|
||||
"detox": {
|
||||
"version": "19.7.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.5.7": {
|
||||
"version": "14.5.7-beta.0",
|
||||
"packages": {
|
||||
"detox": {
|
||||
"version": "19.9.3",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@testing-library/jest-dom": {
|
||||
"version": "5.16.5",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.6.1": {
|
||||
"version": "14.6.1-beta.0",
|
||||
"packages": {
|
||||
"detox": {
|
||||
"version": "19.10.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.7.4": {
|
||||
"version": "14.7.4-beta.0",
|
||||
"packages": {
|
||||
"detox": {
|
||||
"version": "19.12.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"15.0.0": {
|
||||
"version": "15.0.0-beta.0",
|
||||
"packages": {
|
||||
|
||||
@ -1,37 +0,0 @@
|
||||
import { addProjectConfiguration, readJson, Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
|
||||
import update from './add-verbose-jest-config-13-10-3';
|
||||
|
||||
describe('Set verbose to true for jest.config.json for detox apps', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
addProjectConfiguration(tree, 'products', {
|
||||
root: 'apps/products',
|
||||
sourceRoot: 'apps/products/src',
|
||||
targets: {
|
||||
'test-ios': {
|
||||
executor: '@nrwl/detox:test',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it(`should add verbose true to jest.config.json`, async () => {
|
||||
tree.write('apps/products/jest.config.json', '{}');
|
||||
await update(tree);
|
||||
|
||||
const jestConfig = readJson(tree, 'apps/products/jest.config.json');
|
||||
expect(jestConfig.verbose).toEqual(true);
|
||||
});
|
||||
|
||||
it(`should change verbose to true in jest.config.json`, async () => {
|
||||
tree.write('apps/products/jest.config.json', '{"verbose": false}');
|
||||
await update(tree);
|
||||
|
||||
const jestConfig = readJson(tree, 'apps/products/jest.config.json');
|
||||
expect(jestConfig.verbose).toEqual(true);
|
||||
});
|
||||
});
|
||||
@ -1,32 +0,0 @@
|
||||
import {
|
||||
Tree,
|
||||
formatFiles,
|
||||
getProjects,
|
||||
updateJson,
|
||||
ProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
|
||||
/**
|
||||
* Update jest.config.json under detox project, add key verbsoe: true
|
||||
*/
|
||||
export default async function update(tree: Tree) {
|
||||
const projects = getProjects(tree);
|
||||
|
||||
projects.forEach((project) => {
|
||||
if (project.targets?.['test-ios']?.executor !== '@nrwl/detox:test') return;
|
||||
updateJestConfig(tree, project);
|
||||
});
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
function updateJestConfig(host: Tree, project: ProjectConfiguration) {
|
||||
const jestConfigPath = `${project.root}/jest.config.json`;
|
||||
if (!host.exists(jestConfigPath)) return;
|
||||
updateJson(host, jestConfigPath, (json) => {
|
||||
if (!json.verbose) {
|
||||
json.verbose = true;
|
||||
}
|
||||
return json;
|
||||
});
|
||||
}
|
||||
@ -1,73 +0,0 @@
|
||||
import { addProjectConfiguration, getProjects, Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import update from './add-build-target-test-13-5-0';
|
||||
|
||||
describe('add-e2e-targets-13-5-0', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
addProjectConfiguration(tree, 'products-e2e', {
|
||||
root: 'apps/products-e2e',
|
||||
sourceRoot: 'apps/products-e2e/src',
|
||||
targets: {
|
||||
'test-ios': {
|
||||
executor: '@nrwl/detox:test',
|
||||
options: {
|
||||
detoxConfiguration: 'ios.sim.debug',
|
||||
},
|
||||
configurations: {
|
||||
production: {
|
||||
detoxConfiguration: 'ios.sim.release',
|
||||
},
|
||||
},
|
||||
},
|
||||
'test-android': {
|
||||
executor: '@nrwl/detox:test',
|
||||
options: {
|
||||
detoxConfiguration: 'android.emu.debug',
|
||||
},
|
||||
configurations: {
|
||||
production: {
|
||||
detoxConfiguration: 'android.emu.release',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it(`should update project.json with targets e2e`, async () => {
|
||||
await update(tree);
|
||||
|
||||
getProjects(tree).forEach((project) => {
|
||||
expect(project.targets['test-ios']).toEqual({
|
||||
executor: '@nrwl/detox:test',
|
||||
options: {
|
||||
detoxConfiguration: 'ios.sim.debug',
|
||||
buildTarget: 'products-e2e:build-ios',
|
||||
},
|
||||
configurations: {
|
||||
production: {
|
||||
detoxConfiguration: 'ios.sim.release',
|
||||
buildTarget: 'products-e2e:build-ios:prod',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(project.targets['test-android']).toEqual({
|
||||
executor: '@nrwl/detox:test',
|
||||
options: {
|
||||
detoxConfiguration: 'android.emu.debug',
|
||||
buildTarget: 'products-e2e:build-android',
|
||||
},
|
||||
configurations: {
|
||||
production: {
|
||||
detoxConfiguration: 'android.emu.release',
|
||||
buildTarget: 'products-e2e:build-android:prod',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,35 +0,0 @@
|
||||
import {
|
||||
Tree,
|
||||
formatFiles,
|
||||
getProjects,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
|
||||
/**
|
||||
* This function buildTarget to test-ios and test-android
|
||||
*/
|
||||
export default async function update(tree: Tree) {
|
||||
const projects = getProjects(tree);
|
||||
|
||||
for (const [name, config] of projects.entries()) {
|
||||
if (config.targets?.['test-ios']?.executor === '@nrwl/detox:test') {
|
||||
config.targets['test-ios'].options.buildTarget = `${name}:build-ios`;
|
||||
config.targets[
|
||||
'test-ios'
|
||||
].configurations.production.buildTarget = `${name}:build-ios:prod`;
|
||||
}
|
||||
|
||||
if (config.targets?.['test-android']?.executor === '@nrwl/detox:test') {
|
||||
config.targets[
|
||||
'test-android'
|
||||
].options.buildTarget = `${name}:build-android`;
|
||||
config.targets[
|
||||
'test-android'
|
||||
].configurations.production.buildTarget = `${name}:build-android:prod`;
|
||||
}
|
||||
|
||||
updateProjectConfiguration(tree, name, config);
|
||||
}
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
@ -1,35 +0,0 @@
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
readJson,
|
||||
Tree,
|
||||
updateJson,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import update from './remove-types-detox-13-8-2';
|
||||
|
||||
describe('remove-types-detox-13-8-2', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
updateJson(tree, 'package.json', (packageJson) => {
|
||||
packageJson.devDependencies['@types/detox'] = '*';
|
||||
return packageJson;
|
||||
});
|
||||
addProjectConfiguration(tree, 'products-e2e', {
|
||||
root: 'apps/products-e2e',
|
||||
sourceRoot: 'apps/products-e2e/src',
|
||||
targets: {
|
||||
'test-ios': {
|
||||
executor: '@nrwl/detox:test',
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it(`should remove @types/detox from package.json`, async () => {
|
||||
await update(tree);
|
||||
const packageJson = readJson(tree, 'package.json');
|
||||
expect(packageJson.devDependencies['@types/detox']).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@ -1,26 +0,0 @@
|
||||
import { getProjects, readJson, Tree, updateJson } from '@nx/devkit';
|
||||
|
||||
export default async function update(tree: Tree) {
|
||||
const packageJson = readJson(tree, 'package.json');
|
||||
|
||||
if (!packageJson.devDependencies['@types/detox']) {
|
||||
return;
|
||||
}
|
||||
|
||||
const projects = getProjects(tree);
|
||||
const hasDetoxProject = Array.from(projects)
|
||||
.map(([_, project]) => project)
|
||||
.some(
|
||||
(project) =>
|
||||
project.targets?.['test-ios']?.executor === '@nrwl/detox:test'
|
||||
);
|
||||
|
||||
if (!hasDetoxProject) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateJson(tree, 'package.json', (packageJson) => {
|
||||
delete packageJson.devDependencies['@types/detox'];
|
||||
return packageJson;
|
||||
});
|
||||
}
|
||||
@ -1,29 +1,5 @@
|
||||
{
|
||||
"generators": {
|
||||
"add-project-root-metro-config-14-0-0": {
|
||||
"version": "14.0.1-beta.0",
|
||||
"cli": "nx",
|
||||
"description": "Add projectRoot option in metro.config.js",
|
||||
"factory": "./src/migrations/update-14-0-0/add-project-root-metro-config-14-0-0"
|
||||
},
|
||||
"add-eject-target-14-1-2": {
|
||||
"version": "14.1.2-beta.0",
|
||||
"cli": "nx",
|
||||
"description": "Add target eject for expo projects in project.json",
|
||||
"factory": "./src/migrations/update-14-1-2/add-eject-target-14-1-2"
|
||||
},
|
||||
"add-build-target-14-4-3": {
|
||||
"version": "14.4.3-beta.0",
|
||||
"cli": "nx",
|
||||
"description": "Add target build and build-list for expo projects in project.json",
|
||||
"factory": "./src/migrations/update-14-4-3/add-eas-build-target"
|
||||
},
|
||||
"add-update-target-14-5-1": {
|
||||
"version": "14.5.1-beta.0",
|
||||
"cli": "nx",
|
||||
"description": "Add target update for expo projects in project.json",
|
||||
"factory": "./src/migrations/update-14-5-1/add-eas-update-target"
|
||||
},
|
||||
"change-expo-jest-preset": {
|
||||
"version": "15.0.3-beta.0",
|
||||
"cli": "nx",
|
||||
@ -92,391 +68,6 @@
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
"13.8.6": {
|
||||
"version": "13.8.6-beta.0",
|
||||
"packages": {
|
||||
"expo-cli": {
|
||||
"version": "5.3.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.0.0": {
|
||||
"version": "14.0.0-beta.0",
|
||||
"packages": {
|
||||
"expo-cli": {
|
||||
"version": "5.4.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"babel-preset-expo": {
|
||||
"version": "~9.0.2",
|
||||
"alwaysAddToPackageJson": false,
|
||||
"addToPackageJson": "devDependencies"
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.0.1": {
|
||||
"version": "14.0.1-beta.0",
|
||||
"packages": {
|
||||
"expo-cli": {
|
||||
"version": "5.4.3",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.0.2": {
|
||||
"version": "14.0.2-beta.0",
|
||||
"packages": {
|
||||
"metro-resolver": {
|
||||
"version": "0.70.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-dev-client": {
|
||||
"version": "0.8.5",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@expo/metro-config": {
|
||||
"version": "0.3.16",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-updates": {
|
||||
"version": "~0.11.7",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.1.1": {
|
||||
"version": "14.1.1-beta.0",
|
||||
"packages": {
|
||||
"expo": {
|
||||
"version": "45.0.4",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-dev-client": {
|
||||
"version": "~0.9.6",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-status-bar": {
|
||||
"version": "~1.3.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@expo/metro-config": {
|
||||
"version": "0.3.17",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-splash-screen": {
|
||||
"version": "0.15.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-updates": {
|
||||
"version": "~0.13.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"jest-expo": {
|
||||
"version": "45.0.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-cli": {
|
||||
"version": "5.4.6",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"babel-preset-expo": {
|
||||
"version": "~9.1.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native": {
|
||||
"version": "0.68.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@types/react-native": {
|
||||
"version": "0.67.7",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native-web": {
|
||||
"version": "0.17.7",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native-gesture-handler": {
|
||||
"version": "~2.2.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native-reanimated": {
|
||||
"version": "~2.8.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native-safe-area-context": {
|
||||
"version": "4.2.4",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native-screens": {
|
||||
"version": "~3.11.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native-svg": {
|
||||
"version": "12.3.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"metro-resolver": {
|
||||
"version": "0.70.3",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@testing-library/react-native": {
|
||||
"version": "9.1.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@testing-library/jest-native": {
|
||||
"version": "4.0.5",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.1.2": {
|
||||
"version": "14.1.2-beta.0",
|
||||
"packages": {
|
||||
"expo": {
|
||||
"version": "45.0.5",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-cli": {
|
||||
"version": "5.4.9",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"metro-resolver": {
|
||||
"version": "0.71.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"metro-babel-register": {
|
||||
"version": "0.71.0",
|
||||
"alwaysAddToPackageJson": false,
|
||||
"addToPackageJson": "devDependencies"
|
||||
},
|
||||
"react-test-renderer": {
|
||||
"version": "18.1.0",
|
||||
"alwaysAddToPackageJson": false,
|
||||
"addToPackageJson": "devDependencies"
|
||||
},
|
||||
"expo-updates": {
|
||||
"version": "~0.13.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@types/react-native": {
|
||||
"version": "0.67.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.2.3": {
|
||||
"version": "14.2.3-beta.0",
|
||||
"packages": {
|
||||
"expo-dev-client": {
|
||||
"version": "~0.10.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-structured-headers": {
|
||||
"version": "~2.2.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.2.4": {
|
||||
"version": "14.2.4-beta.0",
|
||||
"packages": {
|
||||
"expo-dev-client": {
|
||||
"version": "~1.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.3.2": {
|
||||
"version": "14.3.2-beta.0",
|
||||
"packages": {
|
||||
"expo": {
|
||||
"version": "45.0.6",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-cli": {
|
||||
"version": "5.4.11",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@types/react-native": {
|
||||
"version": "0.68.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.4.3": {
|
||||
"version": "14.4.3-beta.0",
|
||||
"packages": {
|
||||
"eas-cli": {
|
||||
"version": "0.55.1",
|
||||
"alwaysAddToPackageJson": false,
|
||||
"addToPackageJson": "devDependencies"
|
||||
},
|
||||
"expo-cli": {
|
||||
"version": "5.5.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.5.1": {
|
||||
"version": "14.5.1-beta.0",
|
||||
"packages": {
|
||||
"expo": {
|
||||
"version": "46.0.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-dev-client": {
|
||||
"version": "~1.1.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-status-bar": {
|
||||
"version": "~1.4.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@expo/metro-config": {
|
||||
"version": "0.3.21",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-splash-screen": {
|
||||
"version": "~0.16.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-updates": {
|
||||
"version": "~0.14.3",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"jest-expo": {
|
||||
"version": "46.0.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-cli": {
|
||||
"version": "6.0.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"eas-cli": {
|
||||
"version": "0.57.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"babel-preset-expo": {
|
||||
"version": "~9.2.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native": {
|
||||
"version": "0.69.3",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@types/react-native": {
|
||||
"version": "0.69.5",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native-web": {
|
||||
"version": "~0.18.7",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native-gesture-handler": {
|
||||
"version": "~2.5.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native-reanimated": {
|
||||
"version": "~2.9.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native-safe-area-context": {
|
||||
"version": "4.3.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native-screens": {
|
||||
"version": "~3.15.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native-svg": {
|
||||
"version": "12.4.3",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@svgr/webpack": {
|
||||
"version": "^6.3.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"metro-resolver": {
|
||||
"version": "0.72.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"metro-babel-register": {
|
||||
"version": "0.72.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@testing-library/react-native": {
|
||||
"version": "11.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.5.2": {
|
||||
"version": "14.5.2-beta.0",
|
||||
"packages": {
|
||||
"expo": {
|
||||
"version": "46.0.9",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expo-cli": {
|
||||
"version": "6.0.5",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"eas-cli": {
|
||||
"version": "1.1.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native": {
|
||||
"version": "0.69.4",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native-svg": {
|
||||
"version": "13.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"metro-resolver": {
|
||||
"version": "0.72.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@testing-library/jest-native": {
|
||||
"version": "4.0.11",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@expo/metro-config": {
|
||||
"version": "0.3.22",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.5.3": {
|
||||
"version": "14.5.3-beta.0",
|
||||
"packages": {
|
||||
"expo": {
|
||||
"version": "46.0.10",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"eas-cli": {
|
||||
"version": "2.1.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native": {
|
||||
"version": "0.69.5",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@types/react-native": {
|
||||
"version": "0.69.8",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"react-native-svg": {
|
||||
"version": "13.1.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"metro-resolver": {
|
||||
"version": "0.72.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"15.0.0": {
|
||||
"version": "15.0.0-beta.0",
|
||||
"packages": {
|
||||
|
||||
@ -1,192 +0,0 @@
|
||||
import { addProjectConfiguration, readJson, Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
|
||||
import update from './add-project-root-metro-config-14-0-0';
|
||||
|
||||
describe('Add projectRoot option in metro.config.js', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
addProjectConfiguration(tree, 'products', {
|
||||
root: 'apps/products',
|
||||
sourceRoot: 'apps/products/src',
|
||||
targets: {
|
||||
start: {
|
||||
executor: '@nrwl/react-native:start',
|
||||
options: {
|
||||
port: 8081,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it(`should udpate metro.config.js and add key projectRoot`, async () => {
|
||||
tree.write(
|
||||
'apps/products/metro.config.js',
|
||||
`
|
||||
const { withNxMetro } = require('@nx/react-native');
|
||||
const { getDefaultConfig } = require('metro-config');
|
||||
|
||||
module.exports = (async () => {
|
||||
const {
|
||||
resolver: { sourceExts, assetExts },
|
||||
} = await getDefaultConfig();
|
||||
// console.log(getModulesRunBeforeMainModule);
|
||||
return withNxMetro(
|
||||
{
|
||||
transformer: {
|
||||
getTransformOptions: async () => ({
|
||||
transform: {
|
||||
experimentalImportSupport: false,
|
||||
inlineRequires: true,
|
||||
},
|
||||
}),
|
||||
babelTransformerPath: require.resolve('react-native-svg-transformer'),
|
||||
},
|
||||
resolver: {
|
||||
assetExts: assetExts.filter((ext) => ext !== 'svg'),
|
||||
sourceExts: [...sourceExts, 'svg'],
|
||||
resolverMainFields: ['sbmodern', 'browser', 'main'],
|
||||
},
|
||||
},
|
||||
{
|
||||
// Change this to true to see debugging info.
|
||||
// Useful if you have issues resolving modules
|
||||
debug: false,
|
||||
// all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx'
|
||||
extensions: [],
|
||||
}
|
||||
);
|
||||
})();
|
||||
`
|
||||
);
|
||||
await update(tree);
|
||||
|
||||
expect(tree.read('apps/products/metro.config.js', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"const { withNxMetro } = require('@nx/react-native');
|
||||
const { getDefaultConfig } = require('metro-config');
|
||||
|
||||
module.exports = (async () => {
|
||||
const {
|
||||
resolver: { sourceExts, assetExts },
|
||||
} = await getDefaultConfig();
|
||||
// console.log(getModulesRunBeforeMainModule);
|
||||
return withNxMetro(
|
||||
{
|
||||
transformer: {
|
||||
getTransformOptions: async () => ({
|
||||
transform: {
|
||||
experimentalImportSupport: false,
|
||||
inlineRequires: true,
|
||||
},
|
||||
}),
|
||||
babelTransformerPath: require.resolve('react-native-svg-transformer'),
|
||||
},
|
||||
resolver: {
|
||||
assetExts: assetExts.filter((ext) => ext !== 'svg'),
|
||||
sourceExts: [...sourceExts, 'svg'],
|
||||
resolverMainFields: ['sbmodern', 'browser', 'main'],
|
||||
},
|
||||
},
|
||||
{
|
||||
// Change this to true to see debugging info.
|
||||
// Useful if you have issues resolving modules
|
||||
projectRoot: __dirname,
|
||||
watchFolders: [],
|
||||
debug: false,
|
||||
// all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx'
|
||||
extensions: [],
|
||||
}
|
||||
);
|
||||
})();
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it(`should not udpate metro.config.js if projectRoot already exists`, async () => {
|
||||
tree.write(
|
||||
'apps/products/metro.config.js',
|
||||
`
|
||||
const { withNxMetro } = require('@nx/react-native');
|
||||
const { getDefaultConfig } = require('metro-config');
|
||||
|
||||
module.exports = (async () => {
|
||||
const {
|
||||
resolver: { sourceExts, assetExts },
|
||||
} = await getDefaultConfig();
|
||||
// console.log(getModulesRunBeforeMainModule);
|
||||
return withNxMetro(
|
||||
{
|
||||
transformer: {
|
||||
getTransformOptions: async () => ({
|
||||
transform: {
|
||||
experimentalImportSupport: false,
|
||||
inlineRequires: true,
|
||||
},
|
||||
}),
|
||||
babelTransformerPath: require.resolve('react-native-svg-transformer'),
|
||||
},
|
||||
resolver: {
|
||||
assetExts: assetExts.filter((ext) => ext !== 'svg'),
|
||||
sourceExts: [...sourceExts, 'svg'],
|
||||
resolverMainFields: ['sbmodern', 'browser', 'main'],
|
||||
},
|
||||
},
|
||||
{
|
||||
projectRoot: __dirname,
|
||||
// Change this to true to see debugging info.
|
||||
// Useful if you have issues resolving modules
|
||||
debug: false,
|
||||
// all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx'
|
||||
extensions: [],
|
||||
}
|
||||
);
|
||||
})();
|
||||
`
|
||||
);
|
||||
await update(tree);
|
||||
|
||||
expect(tree.read('apps/products/metro.config.js', 'utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"const { withNxMetro } = require('@nx/react-native');
|
||||
const { getDefaultConfig } = require('metro-config');
|
||||
|
||||
module.exports = (async () => {
|
||||
const {
|
||||
resolver: { sourceExts, assetExts },
|
||||
} = await getDefaultConfig();
|
||||
// console.log(getModulesRunBeforeMainModule);
|
||||
return withNxMetro(
|
||||
{
|
||||
transformer: {
|
||||
getTransformOptions: async () => ({
|
||||
transform: {
|
||||
experimentalImportSupport: false,
|
||||
inlineRequires: true,
|
||||
},
|
||||
}),
|
||||
babelTransformerPath: require.resolve('react-native-svg-transformer'),
|
||||
},
|
||||
resolver: {
|
||||
assetExts: assetExts.filter((ext) => ext !== 'svg'),
|
||||
sourceExts: [...sourceExts, 'svg'],
|
||||
resolverMainFields: ['sbmodern', 'browser', 'main'],
|
||||
},
|
||||
},
|
||||
{
|
||||
projectRoot: __dirname,
|
||||
// Change this to true to see debugging info.
|
||||
// Useful if you have issues resolving modules
|
||||
debug: false,
|
||||
// all the file extensions used for imports other than 'ts', 'tsx', 'js', 'jsx'
|
||||
extensions: [],
|
||||
}
|
||||
);
|
||||
})();
|
||||
"
|
||||
`);
|
||||
});
|
||||
});
|
||||
@ -1,43 +0,0 @@
|
||||
import {
|
||||
formatFiles,
|
||||
getProjects,
|
||||
logger,
|
||||
stripIndents,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
|
||||
/**
|
||||
* Add projectRoot and watchFolders options in metro.config.js
|
||||
* @param tree
|
||||
*/
|
||||
export default async function update(tree: Tree) {
|
||||
const projects = getProjects(tree);
|
||||
|
||||
projects.forEach((project) => {
|
||||
const metroConfigPath = `${project.root}/metro.config.js`;
|
||||
if (
|
||||
project.targets?.start?.executor !== '@nrwl/react-native:start' ||
|
||||
!tree.exists(metroConfigPath)
|
||||
)
|
||||
return;
|
||||
try {
|
||||
const metroConfigContent = tree.read(metroConfigPath, 'utf-8');
|
||||
if (metroConfigContent.includes('projectRoot: __dirname')) {
|
||||
return;
|
||||
}
|
||||
if (metroConfigContent.includes('projectRoot')) return;
|
||||
tree.write(
|
||||
metroConfigPath,
|
||||
metroConfigContent.replace(
|
||||
'debug: ',
|
||||
'projectRoot: __dirname, watchFolders: [], debug: '
|
||||
)
|
||||
);
|
||||
} catch {
|
||||
logger.error(
|
||||
stripIndents`Unable to update ${metroConfigPath} for project ${project.root}.`
|
||||
);
|
||||
}
|
||||
});
|
||||
await formatFiles(tree);
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
import { addProjectConfiguration, getProjects, Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import update from './add-eject-target-14-1-2';
|
||||
|
||||
describe('add-eject-target-14-1-2', () => {
|
||||
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 eject`, async () => {
|
||||
await update(tree);
|
||||
|
||||
getProjects(tree).forEach((project) => {
|
||||
expect(project.targets['eject']).toEqual({
|
||||
executor: '@nrwl/expo:eject',
|
||||
options: {},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,26 +0,0 @@
|
||||
import {
|
||||
Tree,
|
||||
formatFiles,
|
||||
getProjects,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
|
||||
/**
|
||||
* Add eject to targets for expo app
|
||||
*/
|
||||
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') {
|
||||
config.targets['eject'] = {
|
||||
executor: '@nrwl/expo:eject',
|
||||
options: {},
|
||||
};
|
||||
}
|
||||
|
||||
updateProjectConfiguration(tree, name, config);
|
||||
}
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
@ -1,41 +0,0 @@
|
||||
import { addProjectConfiguration, getProjects, Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import update from './add-eas-build-target';
|
||||
|
||||
describe('add-eas-build-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 build and build-list`, async () => {
|
||||
await update(tree);
|
||||
|
||||
getProjects(tree).forEach((project) => {
|
||||
expect(project.targets['build']).toEqual({
|
||||
executor: '@nrwl/expo:build',
|
||||
options: {},
|
||||
});
|
||||
expect(project.targets['build-list']).toEqual({
|
||||
executor: '@nrwl/expo:build-list',
|
||||
options: {},
|
||||
});
|
||||
expect(project.targets['download']).toEqual({
|
||||
executor: '@nrwl/expo:download',
|
||||
options: {
|
||||
output: 'apps/product/dist',
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,43 +0,0 @@
|
||||
import {
|
||||
Tree,
|
||||
formatFiles,
|
||||
getProjects,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { join } from 'path';
|
||||
|
||||
/**
|
||||
* Add eas build and build-list target for expo
|
||||
*/
|
||||
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['build']) {
|
||||
config.targets['build'] = {
|
||||
executor: '@nrwl/expo:build',
|
||||
options: {},
|
||||
};
|
||||
}
|
||||
if (!config.targets['build-list']) {
|
||||
config.targets['build-list'] = {
|
||||
executor: '@nrwl/expo:build-list',
|
||||
options: {},
|
||||
};
|
||||
}
|
||||
if (!config.targets['download']) {
|
||||
config.targets['download'] = {
|
||||
executor: '@nrwl/expo:download',
|
||||
options: {
|
||||
output: join(config.root, 'dist'),
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
updateProjectConfiguration(tree, name, config);
|
||||
}
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
@ -1,31 +0,0 @@
|
||||
import { addProjectConfiguration, getProjects, Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import update from './add-eas-update-target';
|
||||
|
||||
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 update`, async () => {
|
||||
await update(tree);
|
||||
|
||||
getProjects(tree).forEach((project) => {
|
||||
expect(project.targets['update']).toEqual({
|
||||
executor: '@nrwl/expo:update',
|
||||
options: {},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,27 +0,0 @@
|
||||
import {
|
||||
Tree,
|
||||
formatFiles,
|
||||
getProjects,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
|
||||
/**
|
||||
* Add eas update target for expo
|
||||
*/
|
||||
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['update']) {
|
||||
config.targets['update'] = {
|
||||
executor: '@nrwl/expo:update',
|
||||
options: {},
|
||||
};
|
||||
}
|
||||
updateProjectConfiguration(tree, name, config);
|
||||
}
|
||||
}
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
@ -7,28 +7,5 @@
|
||||
"implementation": "./src/migrations/update-16-0-0-add-nx-packages/update-16-0-0-add-nx-packages"
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
"13.7.0": {
|
||||
"version": "13.7.0",
|
||||
"packages": {
|
||||
"express": {
|
||||
"version": "4.17.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@types/express": {
|
||||
"version": "4.17.13",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.5.0": {
|
||||
"version": "14.5.0-beta.4",
|
||||
"packages": {
|
||||
"express": {
|
||||
"version": "^4.18.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"packageJsonUpdates": {}
|
||||
}
|
||||
|
||||
@ -1,77 +1,5 @@
|
||||
{
|
||||
"generators": {
|
||||
"update-jest-preset-angular-8-4-0": {
|
||||
"version": "12.1.0-beta.1",
|
||||
"cli": "nx",
|
||||
"description": "Update jest-preset-angular to version 8.4.0",
|
||||
"factory": "./src/migrations/update-12-1-2/update-jest-preset-angular"
|
||||
},
|
||||
"update-ts-jest-6-5-5": {
|
||||
"version": "12.1.2-beta.1",
|
||||
"cli": "nx",
|
||||
"description": "Replace tsConfig with tsconfig for ts-jest in jest.config.js",
|
||||
"factory": "./src/migrations/update-12-1-2/update-ts-jest"
|
||||
},
|
||||
"support-jest-27": {
|
||||
"version": "12.4.0-beta.1",
|
||||
"cli": "nx",
|
||||
"description": "Add testEnvironment: 'jsdom' in web apps + libraries",
|
||||
"factory": "./src/migrations/update-12-4-0/add-test-environment-for-node"
|
||||
},
|
||||
"update-ts-jest-and-jest-preset-angular": {
|
||||
"version": "12.4.0-beta.1",
|
||||
"cli": "nx",
|
||||
"description": "Support for Jest 27 via updating ts-jest + jest-preset-angular",
|
||||
"factory": "./src/migrations/update-12-4-0/update-jest-preset-angular"
|
||||
},
|
||||
"update-jest-config-to-use-util": {
|
||||
"version": "12.6.0-beta.0",
|
||||
"cli": "nx",
|
||||
"description": "Uses `getJestProjects()` to populate projects array in root level `jest.config.js` file.",
|
||||
"factory": "./src/migrations/update-12-6-0/update-base-jest-config"
|
||||
},
|
||||
"update-ts-config-for-test-filenames": {
|
||||
"version": "13.1.2-beta.0",
|
||||
"cli": "nx",
|
||||
"description": "Support .test. file names in tsconfigs",
|
||||
"factory": "./src/migrations/update-13-1-2/update-tsconfigs-for-tests"
|
||||
},
|
||||
"add-missing-root-babel-config": {
|
||||
"version": "13.4.4-beta.0",
|
||||
"cli": "nx",
|
||||
"description": "Create a root babel config file if it doesn't exist and using babel-jest in jest.config.js and add @nrwl/web as needed",
|
||||
"factory": "./src/migrations/update-13-4-4/add-missing-root-babel-config"
|
||||
},
|
||||
"update-jest-config-extensions": {
|
||||
"version": "14.0.0-beta.2",
|
||||
"cli": "nx",
|
||||
"description": "Update move jest config files to .ts files.",
|
||||
"factory": "./src/migrations/update-14-0-0/update-jest-config-ext"
|
||||
},
|
||||
"update-to-export-default": {
|
||||
"version": "14.1.5-beta.0",
|
||||
"cli": "nx",
|
||||
"description": "Update to export default in jest config and revert jest.preset.ts to jest.preset.js",
|
||||
"factory": "./src/migrations/update-14-1-5/update-exports-jest-config"
|
||||
},
|
||||
"exclude-jest-config-from-ts-config": {
|
||||
"version": "14.5.5-beta.0",
|
||||
"cli": "nx",
|
||||
"description": "Exclude jest.config.ts from tsconfig where missing.",
|
||||
"factory": "./src/migrations/update-14-0-0/update-jest-config-ext"
|
||||
},
|
||||
"update-configs-jest-28": {
|
||||
"version": "14.6.0-beta.0",
|
||||
"cli": "nx",
|
||||
"description": "Update jest configs to support jest 28 changes (https://jestjs.io/docs/upgrading-to-jest28#configuration-options)",
|
||||
"factory": "./src/migrations/update-14-6-0/update-configs-jest-28"
|
||||
},
|
||||
"update-tests-jest-28": {
|
||||
"version": "14.6.0-beta.0",
|
||||
"cli": "nx",
|
||||
"description": "Update jest test files to support jest 28 changes (https://jestjs.io/docs/upgrading-to-jest28)",
|
||||
"factory": "./src/migrations/update-14-6-0/update-tests-jest-28"
|
||||
},
|
||||
"add-jest-inputs": {
|
||||
"version": "15.0.0-beta.0",
|
||||
"cli": "nx",
|
||||
@ -104,133 +32,6 @@
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
"12.1.0": {
|
||||
"version": "12.1.0-beta.1",
|
||||
"packages": {
|
||||
"jest-preset-angular": {
|
||||
"version": "8.4.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"ts-jest": {
|
||||
"version": "26.5.5",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"12.4.0": {
|
||||
"version": "12.4.0-beta.1",
|
||||
"packages": {
|
||||
"jest": {
|
||||
"version": "27.0.3",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"ts-jest": {
|
||||
"version": "27.0.3",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"jest-preset-angular": {
|
||||
"version": "9.0.3",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"12.10.0": {
|
||||
"version": "12.10.0-beta.1",
|
||||
"packages": {
|
||||
"jest": {
|
||||
"version": "27.2.3",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@types/jest": {
|
||||
"version": "27.0.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"ts-jest": {
|
||||
"version": "27.0.5",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"babel-jest": {
|
||||
"version": "27.2.3",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.0.0": {
|
||||
"version": "14.0.0-beta.2",
|
||||
"packages": {
|
||||
"jest": {
|
||||
"version": "27.5.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@types/jest": {
|
||||
"version": "27.4.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"ts-jest": {
|
||||
"version": "27.1.4",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"babel-jest": {
|
||||
"version": "27.5.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.2.0": {
|
||||
"version": "14.2.0-rc.2",
|
||||
"packages": {
|
||||
"ts-node": {
|
||||
"version": "~10.8.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.5.5": {
|
||||
"version": "14.5.5-beta.0",
|
||||
"packages": {
|
||||
"ts-node": {
|
||||
"version": "10.9.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.6.0": {
|
||||
"version": "14.6.0-beta.0",
|
||||
"packages": {
|
||||
"jest": {
|
||||
"version": "~28.1.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@types/jest": {
|
||||
"version": "~28.1.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"expect": {
|
||||
"version": "~28.1.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@jest/globals": {
|
||||
"version": "~28.1.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"jest-jasmine2": {
|
||||
"version": "~28.1.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"jest-environment-jsdom": {
|
||||
"version": "~28.1.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"ts-jest": {
|
||||
"version": "~28.0.5",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"babel-jest": {
|
||||
"version": "~28.1.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"15.0.1-beta.3": {
|
||||
"version": "15.0.1-beta.3",
|
||||
"packages": {
|
||||
|
||||
@ -1,88 +0,0 @@
|
||||
import {
|
||||
formatFiles,
|
||||
logger,
|
||||
readProjectConfiguration,
|
||||
stripIndents,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { join } from 'path';
|
||||
import {
|
||||
addPropertyToJestConfig,
|
||||
removePropertyFromJestConfig,
|
||||
} from '../../utils/config/update-config';
|
||||
import { JestExecutorOptions } from '../../executors/jest/schema';
|
||||
|
||||
function updateJestConfig(tree: Tree) {
|
||||
forEachExecutorOptions<JestExecutorOptions>(
|
||||
tree,
|
||||
'@nrwl/jest:jest',
|
||||
(options, projectName) => {
|
||||
const config = require(join(tree.root, options.jestConfig as string));
|
||||
|
||||
// migrate serializers
|
||||
if (
|
||||
config.snapshotSerializers &&
|
||||
Array.isArray(config.snapshotSerializers)
|
||||
) {
|
||||
const snapshotSerializers = config.snapshotSerializers.map(
|
||||
(snapshotSerializer) => {
|
||||
switch (snapshotSerializer) {
|
||||
case 'jest-preset-angular/build/AngularNoNgAttributesSnapshotSerializer.js':
|
||||
return 'jest-preset-angular/build/serializers/no-ng-attributes';
|
||||
case 'jest-preset-angular/build/AngularSnapshotSerializer.js':
|
||||
return 'jest-preset-angular/build/serializers/ng-snapshot';
|
||||
case 'jest-preset-angular/build/HTMLCommentSerializer.js':
|
||||
return 'jest-preset-angular/build/serializers/html-comment';
|
||||
default:
|
||||
return snapshotSerializer;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
try {
|
||||
removePropertyFromJestConfig(
|
||||
tree,
|
||||
options.jestConfig as string,
|
||||
'snapshotSerializers'
|
||||
);
|
||||
addPropertyToJestConfig(
|
||||
tree,
|
||||
options.jestConfig as string,
|
||||
'snapshotSerializers',
|
||||
snapshotSerializers
|
||||
);
|
||||
} catch {
|
||||
logger.error(
|
||||
stripIndents`Unable to update snapshotSerializers for project ${projectName}.
|
||||
More information you can check online documentation https://github.com/thymikee/jest-preset-angular/blob/master/CHANGELOG.md#840-2021-03-04`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const { sourceRoot } = readProjectConfiguration(tree, projectName);
|
||||
const setupTestPath = join(sourceRoot, 'test-setup.ts');
|
||||
if (tree.exists(setupTestPath)) {
|
||||
const contents = tree.read(setupTestPath, 'utf-8');
|
||||
tree.write(
|
||||
setupTestPath,
|
||||
contents.replace(
|
||||
`import 'jest-preset-angular';`,
|
||||
`import 'jest-preset-angular/setup-jest';`
|
||||
)
|
||||
);
|
||||
}
|
||||
} catch {
|
||||
logger.error(
|
||||
stripIndents`Unable to update test-setup.ts for project ${projectName}.`
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default async function update(tree) {
|
||||
updateJestConfig(tree);
|
||||
await formatFiles(tree);
|
||||
}
|
||||
@ -1,50 +0,0 @@
|
||||
import { formatFiles, logger, stripIndents, Tree } from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { join } from 'path';
|
||||
import { JestExecutorOptions } from '../../executors/jest/schema';
|
||||
import {
|
||||
addPropertyToJestConfig,
|
||||
removePropertyFromJestConfig,
|
||||
} from '../../utils/config/update-config';
|
||||
|
||||
function updateJestConfig(tree: Tree) {
|
||||
forEachExecutorOptions<JestExecutorOptions>(
|
||||
tree,
|
||||
'@nrwl/jest:jest',
|
||||
(options, project) => {
|
||||
if (!options.jestConfig) {
|
||||
return;
|
||||
}
|
||||
|
||||
const jestConfigPath = options.jestConfig;
|
||||
const config = require(join(tree.root, jestConfigPath));
|
||||
const tsJestConfig = config.globals?.['ts-jest'];
|
||||
if (!(tsJestConfig && tsJestConfig.tsConfig)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
removePropertyFromJestConfig(
|
||||
tree,
|
||||
jestConfigPath,
|
||||
'globals.ts-jest.tsConfig'
|
||||
);
|
||||
addPropertyToJestConfig(
|
||||
tree,
|
||||
jestConfigPath,
|
||||
'globals.ts-jest.tsconfig',
|
||||
tsJestConfig.tsConfig
|
||||
);
|
||||
} catch {
|
||||
logger.error(
|
||||
stripIndents`Unable to update jest.config.js for project ${project}.`
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default async function update(tree: Tree) {
|
||||
updateJestConfig(tree);
|
||||
await formatFiles(tree);
|
||||
}
|
||||
@ -1,57 +0,0 @@
|
||||
import {
|
||||
formatFiles,
|
||||
logger,
|
||||
ProjectConfiguration,
|
||||
readProjectConfiguration,
|
||||
stripIndents,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { join } from 'path';
|
||||
import { JestExecutorOptions } from '../../executors/jest/schema';
|
||||
import { addPropertyToJestConfig } from '../../utils/config/update-config';
|
||||
|
||||
function updateJestConfig(tree: Tree) {
|
||||
forEachExecutorOptions<JestExecutorOptions>(
|
||||
tree,
|
||||
'@nrwl/jest:jest',
|
||||
(options, project) => {
|
||||
if (!options.jestConfig) {
|
||||
return;
|
||||
}
|
||||
|
||||
const jestConfigPath = options.jestConfig;
|
||||
const jestConfig = require(join(tree.root, jestConfigPath));
|
||||
const projectConfig = readProjectConfiguration(tree, project);
|
||||
const testEnvironment = jestConfig.testEnvironment;
|
||||
|
||||
if (testEnvironment || !checkIfNodeProject(projectConfig)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
addPropertyToJestConfig(
|
||||
tree,
|
||||
jestConfigPath,
|
||||
'testEnvironment',
|
||||
'node'
|
||||
);
|
||||
} catch {
|
||||
logger.error(
|
||||
stripIndents`Unable to update jest.config.js for project ${project}.`
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default async function update(tree: Tree) {
|
||||
updateJestConfig(tree);
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
function checkIfNodeProject(config: ProjectConfiguration) {
|
||||
return Object.entries(config.targets).some(([targetName, targetConfig]) =>
|
||||
targetConfig.executor?.includes?.('node')
|
||||
);
|
||||
}
|
||||
@ -1,148 +0,0 @@
|
||||
import { formatFiles, logger, stripIndents, Tree } from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { join } from 'path';
|
||||
import { JestExecutorOptions } from '../../executors/jest/schema';
|
||||
import {
|
||||
addPropertyToJestConfig,
|
||||
removePropertyFromJestConfig,
|
||||
} from '../../utils/config/update-config';
|
||||
|
||||
function updateJestConfig(tree: Tree) {
|
||||
forEachExecutorOptions<JestExecutorOptions>(
|
||||
tree,
|
||||
'@nrwl/jest:jest',
|
||||
(options, project) => {
|
||||
if (!options.jestConfig) {
|
||||
return;
|
||||
}
|
||||
|
||||
const jestConfigPath = options.jestConfig;
|
||||
const jestConfig = require(join(
|
||||
tree.root,
|
||||
jestConfigPath
|
||||
)) as PartialJestConfig;
|
||||
|
||||
if (!usesJestPresetAngular(jestConfig)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
updateASTTransformers(tree, jestConfigPath, jestConfig);
|
||||
updateTransform(tree, jestConfigPath, jestConfig);
|
||||
} catch {
|
||||
logger.error(
|
||||
stripIndents`Unable to update jest.config.js for project ${project}.`
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default async function update(tree: Tree) {
|
||||
updateJestConfig(tree);
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
export function updateASTTransformers(
|
||||
tree: Tree,
|
||||
jestConfigPath: string,
|
||||
jestConfig: PartialJestConfig
|
||||
) {
|
||||
const newTransformers = getNewAstTransformers(
|
||||
jestConfig.globals?.['ts-jest']?.astTransformers
|
||||
);
|
||||
if (newTransformers === null) {
|
||||
removePropertyFromJestConfig(
|
||||
tree,
|
||||
jestConfigPath,
|
||||
'globals.ts-jest.astTransformers'
|
||||
);
|
||||
} else {
|
||||
addPropertyToJestConfig(
|
||||
tree,
|
||||
jestConfigPath,
|
||||
'globals.ts-jest.astTransformers',
|
||||
newTransformers
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function updateTransform(
|
||||
tree: Tree,
|
||||
jestConfigPath: string,
|
||||
jestConfig: PartialJestConfig
|
||||
) {
|
||||
removePropertyFromJestConfig(tree, jestConfigPath, 'transform');
|
||||
addPropertyToJestConfig(tree, jestConfigPath, 'transform', {
|
||||
'^.+\\.(ts|js|html)$': 'jest-preset-angular',
|
||||
});
|
||||
}
|
||||
|
||||
interface PartialJestConfig {
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
astTransformers: ASTTransformers;
|
||||
};
|
||||
};
|
||||
transform?: Record<string, string>;
|
||||
}
|
||||
|
||||
interface ASTTransformer {
|
||||
path: string;
|
||||
options: unknown;
|
||||
}
|
||||
|
||||
interface ASTTransformers {
|
||||
before: (ASTTransformer | string)[];
|
||||
after: (ASTTransformer | string)[];
|
||||
afterDeclarations: (ASTTransformer | string)[];
|
||||
}
|
||||
|
||||
export function getNewAstTransformers(
|
||||
astTransformers: ASTTransformers
|
||||
): ASTTransformers | null {
|
||||
let result = {
|
||||
before: astTransformers?.before?.filter?.(
|
||||
(x) => !transformerIsFromJestPresetAngular(x)
|
||||
),
|
||||
after: astTransformers?.after?.filter?.(
|
||||
(x) => !transformerIsFromJestPresetAngular(x)
|
||||
),
|
||||
afterDeclarations: astTransformers?.afterDeclarations?.filter?.(
|
||||
(x) => !transformerIsFromJestPresetAngular(x)
|
||||
),
|
||||
};
|
||||
|
||||
result = {
|
||||
before: result.before?.length > 0 ? result.before : undefined,
|
||||
after: result.after?.length > 0 ? result.after : undefined,
|
||||
afterDeclarations:
|
||||
result.afterDeclarations?.length > 0
|
||||
? result.afterDeclarations
|
||||
: undefined,
|
||||
};
|
||||
|
||||
if (!result.before && !result.after && !result.afterDeclarations) {
|
||||
return null;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export function transformerIsFromJestPresetAngular(
|
||||
transformer: ASTTransformer | string
|
||||
) {
|
||||
return typeof transformer === 'string'
|
||||
? transformer.includes('jest-preset-angular')
|
||||
: transformer.path.includes('jest-preset-angular');
|
||||
}
|
||||
|
||||
export function usesJestPresetAngular(jestConfig: PartialJestConfig) {
|
||||
const transformers = Array.isArray(
|
||||
jestConfig.globals?.['ts-jest']?.astTransformers
|
||||
)
|
||||
? jestConfig.globals?.['ts-jest']?.astTransformers || []
|
||||
: jestConfig.globals?.['ts-jest']?.astTransformers?.before || [];
|
||||
|
||||
return transformers.some((x) => transformerIsFromJestPresetAngular(x));
|
||||
}
|
||||
@ -1,100 +0,0 @@
|
||||
const mockGetJestProjects = jest.fn(() => []);
|
||||
jest.mock('../../utils/config/get-jest-projects', () => ({
|
||||
getJestProjects: mockGetJestProjects,
|
||||
}));
|
||||
const mockResolveConfig = jest.fn(() =>
|
||||
Promise.resolve({ singleQuote: true, endOfLine: 'lf' })
|
||||
);
|
||||
|
||||
import { Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import update from './update-base-jest-config';
|
||||
|
||||
describe('update 12.6.0', () => {
|
||||
let tree: Tree;
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
tree.write(
|
||||
'jest.config.js',
|
||||
`module.exports = {
|
||||
projects: ['<rootDir>/test-1']
|
||||
}`
|
||||
);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
const prettier = await import('prettier');
|
||||
prettier.resolveConfig = mockResolveConfig as any;
|
||||
});
|
||||
|
||||
test('no projects key configured', async () => {
|
||||
tree.write('jest.config.js', 'module.exports = {}');
|
||||
|
||||
await update(tree);
|
||||
|
||||
const result = tree.read('jest.config.js').toString();
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"const { getJestProjects } = require('@nx/jest');
|
||||
|
||||
module.exports = { projects: getJestProjects() };
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
test('all jest projects covered', async () => {
|
||||
mockGetJestProjects.mockImplementation(() => ['<rootDir>/test-1']);
|
||||
await update(tree);
|
||||
const result = tree.read('jest.config.js').toString();
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"const { getJestProjects } = require('@nx/jest');
|
||||
|
||||
module.exports = { projects: getJestProjects() };
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
test('some jest projects uncovered', async () => {
|
||||
mockGetJestProjects.mockImplementation(() => ['<rootDir>/test-2']);
|
||||
await update(tree);
|
||||
const result = tree.read('jest.config.js').toString();
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"const { getJestProjects } = require('@nx/jest');
|
||||
|
||||
module.exports = { projects: [...getJestProjects(), '<rootDir>/test-1'] };
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
test('proper formatting with multiple uncovered jest projects', async () => {
|
||||
mockGetJestProjects.mockImplementation(() => ['<rootDir>/test-2']);
|
||||
tree.write(
|
||||
'jest.config.js',
|
||||
`
|
||||
module.exports = {
|
||||
projects: [
|
||||
'<rootDir>/test-1',
|
||||
'<rootDir>/test-2',
|
||||
'<rootDir>/test-3',
|
||||
'<rootDir>/test-4',
|
||||
'<rootDir>/test-5'
|
||||
]
|
||||
}`
|
||||
);
|
||||
await update(tree);
|
||||
const result = tree.read('jest.config.js').toString();
|
||||
expect(result).toMatchInlineSnapshot(`
|
||||
"const { getJestProjects } = require('@nx/jest');
|
||||
|
||||
module.exports = {
|
||||
projects: [
|
||||
...getJestProjects(),
|
||||
'<rootDir>/test-1',
|
||||
'<rootDir>/test-3',
|
||||
'<rootDir>/test-4',
|
||||
'<rootDir>/test-5',
|
||||
],
|
||||
};
|
||||
"
|
||||
`);
|
||||
});
|
||||
});
|
||||
@ -1,62 +0,0 @@
|
||||
import { formatFiles, Tree } from '@nx/devkit';
|
||||
import { jestConfigObject } from '../../utils/config/functions';
|
||||
import { getJestProjects } from '../../utils/config/get-jest-projects';
|
||||
import {
|
||||
addImportStatementToJestConfig,
|
||||
addPropertyToJestConfig,
|
||||
removePropertyFromJestConfig,
|
||||
} from '../../utils/config/update-config';
|
||||
|
||||
function determineUncoveredJestProjects(existingProjects: string[]) {
|
||||
const coveredJestProjects = (getJestProjects() as string[]).reduce(
|
||||
(acc, key) => {
|
||||
acc[key] = true;
|
||||
return acc;
|
||||
},
|
||||
{}
|
||||
);
|
||||
return existingProjects.filter((project) => !coveredJestProjects[project]);
|
||||
}
|
||||
|
||||
function determineProjectsValue(uncoveredJestProjects: string[]): string {
|
||||
if (!uncoveredJestProjects.length) {
|
||||
return `getJestProjects()`;
|
||||
}
|
||||
return `[...getJestProjects(), ${uncoveredJestProjects
|
||||
.map((projectName) => `'${projectName}', `)
|
||||
.join('')}]`;
|
||||
}
|
||||
|
||||
function updateBaseJestConfig(
|
||||
tree: Tree,
|
||||
baseJestConfigPath = 'jest.config.js'
|
||||
) {
|
||||
if (tree.read('/jest.config.js', 'utf-8').includes('getJestProjects()')) {
|
||||
return;
|
||||
}
|
||||
const currentConfig = jestConfigObject(tree, baseJestConfigPath);
|
||||
currentConfig.projects ??= [];
|
||||
|
||||
const uncoveredJestProjects = determineUncoveredJestProjects(
|
||||
currentConfig.projects as string[]
|
||||
);
|
||||
removePropertyFromJestConfig(tree, baseJestConfigPath, 'projects');
|
||||
addPropertyToJestConfig(
|
||||
tree,
|
||||
baseJestConfigPath,
|
||||
'projects',
|
||||
determineProjectsValue(uncoveredJestProjects),
|
||||
{ valueAsString: true }
|
||||
);
|
||||
addImportStatementToJestConfig(
|
||||
tree,
|
||||
baseJestConfigPath,
|
||||
`const { getJestProjects } = require('@nx/jest');`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
export default async function update(tree: Tree) {
|
||||
updateBaseJestConfig(tree);
|
||||
await formatFiles(tree);
|
||||
}
|
||||
@ -1,296 +0,0 @@
|
||||
import { addProjectConfiguration, Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import update from './update-tsconfigs-for-tests';
|
||||
|
||||
const reactTsConfigs = {
|
||||
app: {
|
||||
extends: './tsconfig.json',
|
||||
compilerOptions: {
|
||||
outDir: '../../dist/out-tsc',
|
||||
types: ['node'],
|
||||
},
|
||||
files: [
|
||||
'../../node_modules/@nrwl/react/typings/cssmodule.d.ts',
|
||||
'../../node_modules/@nrwl/react/typings/image.d.ts',
|
||||
],
|
||||
exclude: [
|
||||
'**/*.spec.ts',
|
||||
'**/*_spec.ts',
|
||||
'**/*.spec.tsx',
|
||||
'**/*.spec.js',
|
||||
'**/*.spec.jsx',
|
||||
],
|
||||
include: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx'],
|
||||
},
|
||||
lib: {
|
||||
extends: './tsconfig.json',
|
||||
compilerOptions: {
|
||||
outDir: '../../dist/out-tsc',
|
||||
types: ['node'],
|
||||
},
|
||||
files: [
|
||||
'../../node_modules/@nrwl/react/typings/cssmodule.d.ts',
|
||||
'../../node_modules/@nrwl/react/typings/image.d.ts',
|
||||
],
|
||||
exclude: [
|
||||
'**/*.spec.ts',
|
||||
'**/*_spec.ts',
|
||||
'**/*.spec.tsx',
|
||||
'**/*.spec.js',
|
||||
'**/*.spec.jsx',
|
||||
],
|
||||
include: ['**/*.js', '**/*.jsx', '**/*.ts', '**/*.tsx'],
|
||||
},
|
||||
spec: {
|
||||
extends: './tsconfig.json',
|
||||
compilerOptions: {
|
||||
outDir: '../../dist/out-tsc',
|
||||
module: 'commonjs',
|
||||
types: ['jest', 'node'],
|
||||
},
|
||||
include: [
|
||||
'**/*.spec.ts',
|
||||
'**/*_spec.ts',
|
||||
'**/*.spec.tsx',
|
||||
'**/*.spec.js',
|
||||
'**/*.spec.jsx',
|
||||
'**/*.d.ts',
|
||||
],
|
||||
files: [
|
||||
'../../node_modules/@nrwl/react/typings/cssmodule.d.ts',
|
||||
'../../node_modules/@nrwl/react/typings/image.d.ts',
|
||||
],
|
||||
},
|
||||
base: {
|
||||
include: [],
|
||||
files: [],
|
||||
references: [
|
||||
{
|
||||
path: './tsconfig.app.json',
|
||||
},
|
||||
{
|
||||
path: './tsconfig.lib.json',
|
||||
},
|
||||
{
|
||||
path: './tsconfig.spec.json',
|
||||
},
|
||||
],
|
||||
},
|
||||
expectedFilesToContain: [
|
||||
'**/*.spec.ts',
|
||||
'**/*.test.ts',
|
||||
'**/*_spec.ts',
|
||||
'**/*_test.ts',
|
||||
'**/*.spec.tsx',
|
||||
'**/*.test.tsx',
|
||||
'**/*.spec.js',
|
||||
'**/*.test.js',
|
||||
'**/*.spec.jsx',
|
||||
'**/*.test.jsx',
|
||||
],
|
||||
};
|
||||
const angularTsConfigs = {
|
||||
app: {
|
||||
extends: './tsconfig.json',
|
||||
compilerOptions: {
|
||||
outDir: '../../dist/out-tsc',
|
||||
types: [],
|
||||
},
|
||||
files: ['src/main.ts', 'src/polyfills.ts'],
|
||||
include: ['src/**/*.d.ts'],
|
||||
exclude: ['**/*.spec.ts', '**/*_spec.ts'],
|
||||
},
|
||||
lib: {
|
||||
extends: './tsconfig.json',
|
||||
compilerOptions: {
|
||||
outDir: '../../dist/out-tsc',
|
||||
target: 'es2015',
|
||||
declaration: true,
|
||||
declarationMap: true,
|
||||
inlineSources: true,
|
||||
types: [],
|
||||
lib: ['dom', 'es2018'],
|
||||
},
|
||||
exclude: ['src/test-setup.ts', '**/*.spec.ts'],
|
||||
include: ['**/*.ts'],
|
||||
},
|
||||
spec: {
|
||||
extends: './tsconfig.json',
|
||||
compilerOptions: {
|
||||
outDir: '../../dist/out-tsc',
|
||||
module: 'commonjs',
|
||||
types: ['jest', 'node'],
|
||||
},
|
||||
files: ['src/test-setup.ts'],
|
||||
include: ['**/*.spec.ts', '**/*_spec.ts', '**/*.d.ts'],
|
||||
},
|
||||
expectedFilesToContain: [
|
||||
'**/*.spec.ts',
|
||||
'**/*.test.ts',
|
||||
'**/*_spec.ts',
|
||||
'**/*_test.ts',
|
||||
],
|
||||
};
|
||||
|
||||
const tsConfigLibBase = {
|
||||
include: [],
|
||||
files: [],
|
||||
references: [
|
||||
{
|
||||
path: './tsconfig.lib.json',
|
||||
},
|
||||
{
|
||||
path: './tsconfig.spec.json',
|
||||
},
|
||||
],
|
||||
};
|
||||
const tsConfigAppBase = {
|
||||
include: [],
|
||||
files: [],
|
||||
references: [
|
||||
{
|
||||
path: './tsconfig.app.json',
|
||||
},
|
||||
{
|
||||
path: './tsconfig.spec.json',
|
||||
},
|
||||
],
|
||||
};
|
||||
const tsConfigWithExclude = {
|
||||
exclude: ['**/*.spec.ts', '**/*_spec.ts'],
|
||||
};
|
||||
|
||||
[
|
||||
// test TSX/JSX support
|
||||
{ name: 'React App', configs: reactTsConfigs },
|
||||
// test non TSX/JSX support
|
||||
{ name: 'Angular App', configs: angularTsConfigs },
|
||||
].forEach(({ name, configs }) => {
|
||||
describe(`Jest Migration (v13.1.2): ${name}`, () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
tree.write(
|
||||
'apps/project-one/tsconfig.app.json',
|
||||
String.raw`${JSON.stringify(configs.app, null, 2)}`
|
||||
);
|
||||
tree.write(
|
||||
'apps/project-one/tsconfig.spec.json',
|
||||
String.raw`${JSON.stringify(configs.spec, null, 2)}`
|
||||
);
|
||||
tree.write(
|
||||
`apps/project-one/tsconfig.json`,
|
||||
String.raw`${JSON.stringify(tsConfigAppBase, null, 2)}`
|
||||
);
|
||||
tree.write(
|
||||
'libs/lib-one/tsconfig.lib.json',
|
||||
String.raw`${JSON.stringify(configs.lib, null, 2)}`
|
||||
);
|
||||
tree.write(
|
||||
'libs/lib-one/tsconfig.spec.json',
|
||||
String.raw`${JSON.stringify(configs.spec, null, 2)}`
|
||||
);
|
||||
tree.write(
|
||||
`libs/lib-one/tsconfig.json`,
|
||||
String.raw`${JSON.stringify(tsConfigLibBase, null, 2)}`
|
||||
);
|
||||
|
||||
addProjectConfiguration(tree, 'lib-one', {
|
||||
root: 'libs/lib-one',
|
||||
sourceRoot: 'libs/lib-one/src',
|
||||
projectType: 'library',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/web:build',
|
||||
},
|
||||
test: {
|
||||
executor: '@nrwl/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'libs/lib-one/jest.config.js',
|
||||
passWithNoTests: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
addProjectConfiguration(tree, 'project-one', {
|
||||
root: 'apps/project-one',
|
||||
sourceRoot: 'apps/project-one/src',
|
||||
projectType: 'application',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/web:build',
|
||||
},
|
||||
test: {
|
||||
executor: '@nrwl/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'apps/project-one/jest.config.js',
|
||||
passWithNoTests: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should update tsconfig.spec.json to include .test. files', async () => {
|
||||
await update(tree);
|
||||
const tsAppSpecConfig = JSON.parse(
|
||||
tree.read('apps/project-one/tsconfig.spec.json', 'utf-8')
|
||||
);
|
||||
const tsLibSpecConfig = JSON.parse(
|
||||
tree.read('apps/project-one/tsconfig.spec.json', 'utf-8')
|
||||
);
|
||||
|
||||
expect(tsAppSpecConfig.include).toEqual(
|
||||
expect.arrayContaining(configs.expectedFilesToContain)
|
||||
);
|
||||
|
||||
expect(tsLibSpecConfig.include).toEqual(
|
||||
expect.arrayContaining(configs.expectedFilesToContain)
|
||||
);
|
||||
});
|
||||
|
||||
it('should update tsconfig.{lib|app}.json to exclude .test. files', async () => {
|
||||
await update(tree);
|
||||
const tsAppConfig = JSON.parse(
|
||||
tree.read('apps/project-one/tsconfig.app.json', 'utf-8')
|
||||
);
|
||||
const tsLibConfig = JSON.parse(
|
||||
tree.read('apps/project-one/tsconfig.app.json', 'utf-8')
|
||||
);
|
||||
|
||||
expect(tsAppConfig.exclude).toEqual(
|
||||
expect.arrayContaining(configs.expectedFilesToContain)
|
||||
);
|
||||
expect(tsLibConfig.exclude).toEqual(
|
||||
expect.arrayContaining(configs.expectedFilesToContain)
|
||||
);
|
||||
});
|
||||
|
||||
it('should not update tsconfig without spec. patterns for include or exclude', async () => {
|
||||
await update(tree);
|
||||
const tsConfig = JSON.parse(
|
||||
tree.read('apps/project-one/tsconfig.json', 'utf-8')
|
||||
);
|
||||
expect(tsConfig).toEqual(tsConfigAppBase);
|
||||
});
|
||||
|
||||
it('should update any tsconfig with spec pattern for include or exclude', async () => {
|
||||
tree.write(
|
||||
'apps/project-one/tsconfig.random.json',
|
||||
String.raw`${JSON.stringify(tsConfigWithExclude, null, 2)}`
|
||||
);
|
||||
await update(tree);
|
||||
|
||||
const randomTsConfig = JSON.parse(
|
||||
tree.read('apps/project-one/tsconfig.random.json', 'utf-8')
|
||||
);
|
||||
expect(randomTsConfig.exclude).toEqual([
|
||||
'**/*.spec.ts',
|
||||
'**/*.test.ts',
|
||||
'**/*_spec.ts',
|
||||
'**/*_test.ts',
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,85 +0,0 @@
|
||||
import {
|
||||
formatFiles,
|
||||
logger,
|
||||
readProjectConfiguration,
|
||||
stripIndents,
|
||||
Tree,
|
||||
updateJson,
|
||||
visitNotIgnoredFiles,
|
||||
} from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { basename } from 'path';
|
||||
import { JestExecutorOptions } from '../../executors/jest/schema';
|
||||
|
||||
function updateTsConfigsForTests(tree: Tree) {
|
||||
forEachExecutorOptions<JestExecutorOptions>(
|
||||
tree,
|
||||
'@nrwl/jest:jest',
|
||||
(jestOptions, projectName) => {
|
||||
const projectConfig = readProjectConfiguration(tree, projectName);
|
||||
|
||||
visitNotIgnoredFiles(tree, projectConfig.root, (path) => {
|
||||
const fileName = basename(path);
|
||||
if (fileName.startsWith('tsconfig') && fileName.endsWith('.json')) {
|
||||
updateTsConfig(tree, path);
|
||||
}
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
function updateTsConfig(tree: Tree, tsconfigSpecPath: string) {
|
||||
try {
|
||||
updateJson<TsConfig>(tree, tsconfigSpecPath, (value) => {
|
||||
if (value.include) {
|
||||
value.include = makeAllPatternsFromSpecPatterns(value.include);
|
||||
}
|
||||
|
||||
if (value.exclude) {
|
||||
value.exclude = makeAllPatternsFromSpecPatterns(value.exclude);
|
||||
}
|
||||
return value;
|
||||
});
|
||||
} catch (error) {
|
||||
// issue trying to parse the tsconfig file bc it's invalid JSON from template markup/comments
|
||||
// ignore and move on
|
||||
logger.warn(stripIndents`Unable to update ${tsconfigSpecPath}. `);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* take an array of patterns and create patterns from those containing .spec. with .test.
|
||||
* by default the pattern ** /*.spec.ts will be used if no value is passed in.
|
||||
*/
|
||||
function makeAllPatternsFromSpecPatterns(
|
||||
specGlobs: string[] = ['**/*.spec.ts']
|
||||
): string[] {
|
||||
return makeUniquePatterns(
|
||||
specGlobs.reduce((patterns, current) => {
|
||||
patterns.push(current);
|
||||
// .spec. and _spec. can used as testing file name patterns
|
||||
if (current.includes('spec.')) {
|
||||
patterns.push(current.replace('spec.', 'test.'));
|
||||
}
|
||||
return patterns;
|
||||
}, [])
|
||||
);
|
||||
}
|
||||
|
||||
function makeUniquePatterns(items: string[] = []): string[] {
|
||||
return [...new Set(items)];
|
||||
}
|
||||
|
||||
export default async function update(tree: Tree) {
|
||||
updateTsConfigsForTests(tree);
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
interface TsConfig {
|
||||
files?: string[];
|
||||
include?: string[];
|
||||
exclude?: string[];
|
||||
references?: {
|
||||
path: string;
|
||||
}[];
|
||||
}
|
||||
@ -1,113 +0,0 @@
|
||||
import { addProjectConfiguration, readJson, Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import update from './add-missing-root-babel-config';
|
||||
|
||||
describe('Jest Migration (v13.4.4)', () => {
|
||||
let tree: Tree;
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
tree.write(
|
||||
'package.json',
|
||||
JSON.stringify({
|
||||
name: 'test',
|
||||
version: '',
|
||||
description: '',
|
||||
devDependencies: {},
|
||||
})
|
||||
);
|
||||
tree.write(
|
||||
'libs/lib-one/jest.config.js',
|
||||
String.raw`module.exports = {
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': 'babel-jest',
|
||||
}
|
||||
}`
|
||||
);
|
||||
|
||||
addProjectConfiguration(tree, 'lib-one', {
|
||||
root: 'libs/lib-one',
|
||||
sourceRoot: 'libs/lib-one/src',
|
||||
projectType: 'library',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/web:build',
|
||||
},
|
||||
test: {
|
||||
executor: '@nrwl/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'libs/lib-one/jest.config.js',
|
||||
passWithNoTests: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
addProjectConfiguration(tree, 'lib-two', {
|
||||
root: 'libs/lib-two',
|
||||
sourceRoot: 'libs/lib-two/src',
|
||||
projectType: 'library',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/web:build',
|
||||
},
|
||||
test: {
|
||||
executor: '@nrwl/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'libs/lib-two/jest.config.js',
|
||||
passWithNoTests: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should create root babel.config.json and install @nrwl/web', async () => {
|
||||
await update(tree);
|
||||
expect(tree.exists('babel.config.json')).toBeTruthy();
|
||||
const packageJson = readJson(tree, 'package.json');
|
||||
expect(packageJson.devDependencies['@nrwl/web']).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not change anything if root babel.config.json is found', async () => {
|
||||
tree.write('babel.config.json', '{"babelrcRoots": ["*"]}');
|
||||
await update(tree);
|
||||
const packageJson = readJson(tree, 'package.json');
|
||||
expect(packageJson.devDependencies['@nrwl/web']).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should update w/ Array value for babel-jest transformer', async () => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
|
||||
tree.write(
|
||||
'libs/lib-three/jest.config.js',
|
||||
String.raw`module.exports = {
|
||||
transform: {
|
||||
'^.+\\\\.[tj]sx?$': ['babel-jest', {someOptionsThatDontMatter: false}],
|
||||
}
|
||||
}`
|
||||
);
|
||||
|
||||
addProjectConfiguration(tree, 'lib-three', {
|
||||
root: 'libs/lib-three',
|
||||
sourceRoot: 'libs/lib-three/src',
|
||||
projectType: 'library',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/web:build',
|
||||
},
|
||||
test: {
|
||||
executor: '@nrwl/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'libs/lib-three/jest.config.js',
|
||||
passWithNoTests: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await update(tree);
|
||||
expect(tree.exists('babel.config.json')).toBeTruthy();
|
||||
const packageJson = readJson(tree, 'package.json');
|
||||
expect(packageJson.devDependencies['@nrwl/web']).toBeTruthy();
|
||||
});
|
||||
});
|
||||
@ -1,62 +0,0 @@
|
||||
import {
|
||||
addDependenciesToPackageJson,
|
||||
formatFiles,
|
||||
joinPathFragments,
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { JestExecutorOptions } from '../../executors/jest/schema';
|
||||
import { jestConfigObject } from '../../utils/config/functions';
|
||||
import { nxVersion } from '../../utils/versions';
|
||||
|
||||
function checkIfProjectNeedsUpdate(tree: Tree): boolean {
|
||||
if (tree.exists('babel.config.json')) {
|
||||
// the project is already running on babel and good to go
|
||||
return false;
|
||||
}
|
||||
|
||||
let shouldUpdate = false;
|
||||
forEachExecutorOptions<JestExecutorOptions>(
|
||||
tree,
|
||||
'@nrwl/jest:jest',
|
||||
(jestOptions, projectName) => {
|
||||
const projectConfig = readProjectConfiguration(tree, projectName);
|
||||
const jestConfigPath = joinPathFragments(
|
||||
projectConfig.root,
|
||||
'jest.config.js'
|
||||
);
|
||||
|
||||
if (!tree.exists(jestConfigPath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const config = jestConfigObject(tree, jestConfigPath);
|
||||
|
||||
if (config.transform) {
|
||||
for (const transformer of Object.values(config.transform)) {
|
||||
if (
|
||||
(typeof transformer === 'string' && transformer === 'babel-jest') ||
|
||||
(Array.isArray(transformer) && transformer[0] === 'babel-jest')
|
||||
) {
|
||||
shouldUpdate = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return shouldUpdate;
|
||||
}
|
||||
|
||||
export default async function update(tree: Tree) {
|
||||
const shouldUpdateConfigs = checkIfProjectNeedsUpdate(tree);
|
||||
|
||||
if (shouldUpdateConfigs) {
|
||||
addDependenciesToPackageJson(tree, {}, { '@nrwl/web': nxVersion });
|
||||
|
||||
tree.write('babel.config.json', '{"babelrcRoots": ["*"]}');
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
}
|
||||
@ -1,94 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Jest Migration (v14.0.0) should NOT update jest.config.ts preset 1`] = `
|
||||
"/* eslint-disable */
|
||||
|
||||
module.exports = {
|
||||
displayName: 'lib-one',
|
||||
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/lib-one',
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration (v14.0.0) should produce the same results when running multiple times 1`] = `
|
||||
{
|
||||
"files": [
|
||||
"*.ts",
|
||||
"*.tsx",
|
||||
"*.js",
|
||||
"*.jsx",
|
||||
],
|
||||
"parserOptions": {
|
||||
"project": [
|
||||
"libs/my-next-proj/tsconfig.*?.json",
|
||||
],
|
||||
},
|
||||
"rules": {},
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`Jest Migration (v14.0.0) should produce the same results when running multiple times 2`] = `
|
||||
"/* eslint-disable */
|
||||
module.exports = {
|
||||
displayName: 'lib-one',
|
||||
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/lib-one',
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration (v14.0.0) should rename project jest.config.js to jest.config.ts 1`] = `
|
||||
"/* eslint-disable */
|
||||
|
||||
module.exports = {
|
||||
displayName: 'lib-one',
|
||||
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/lib-one',
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration (v14.0.0) should update the excludes of next js apps using the project parser settings 1`] = `
|
||||
{
|
||||
"files": [
|
||||
"*.ts",
|
||||
"*.tsx",
|
||||
"*.js",
|
||||
"*.jsx",
|
||||
],
|
||||
"parserOptions": {
|
||||
"project": [
|
||||
"libs/lib-one/tsconfig.*?.json",
|
||||
],
|
||||
},
|
||||
"rules": {},
|
||||
}
|
||||
`;
|
||||
@ -1,314 +0,0 @@
|
||||
import {
|
||||
readJson,
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
updateJson,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { libraryGenerator as workspaceLib } from '@nx/js';
|
||||
import jestInitGenerator from '../../generators/init/init';
|
||||
import { updateJestConfigExt } from './update-jest-config-ext';
|
||||
|
||||
const setupDefaults = {
|
||||
js: true,
|
||||
skipPackageJson: true,
|
||||
libName: 'lib-one',
|
||||
setParserOptionsProject: false,
|
||||
};
|
||||
const oldConfig = `
|
||||
module.exports = {
|
||||
displayName: 'PLACE_HOLDER',
|
||||
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/PLACE_HOLDER'
|
||||
};
|
||||
`;
|
||||
|
||||
async function libSetUp(tree: Tree, options = setupDefaults) {
|
||||
jestInitGenerator(tree, {
|
||||
js: options.js,
|
||||
skipPackageJson: options.skipPackageJson,
|
||||
});
|
||||
await workspaceLib(tree, {
|
||||
name: options.libName,
|
||||
setParserOptionsProject: options.setParserOptionsProject,
|
||||
});
|
||||
tree.rename(
|
||||
`libs/${options.libName}/jest.config.ts`,
|
||||
`libs/${options.libName}/jest.config.js`
|
||||
);
|
||||
tree.write(
|
||||
`libs/${options.libName}/jest.config.js`,
|
||||
oldConfig.replace(/PLACE_HOLDER/g, options.libName)
|
||||
);
|
||||
updateProjectConfiguration(tree, options.libName, {
|
||||
...readProjectConfiguration(tree, options.libName),
|
||||
targets: {
|
||||
test: {
|
||||
executor: '@nrwl/jest:jest',
|
||||
options: {
|
||||
jestConfig: `libs/${options.libName}/jest.config.js`,
|
||||
passWithNoTests: true,
|
||||
},
|
||||
configurations: {
|
||||
production: {
|
||||
silent: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
describe('Jest Migration (v14.0.0)', () => {
|
||||
let tree: Tree;
|
||||
beforeEach(async () => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
});
|
||||
|
||||
it('should rename project jest.config.js to jest.config.ts', async () => {
|
||||
await libSetUp(tree);
|
||||
|
||||
await updateJestConfigExt(tree);
|
||||
expect(tree.exists('libs/lib-one/jest.config.ts')).toBeTruthy();
|
||||
expect(tree.read('libs/lib-one/jest.config.ts', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should rename root jest.config.js', async () => {
|
||||
await libSetUp(tree);
|
||||
|
||||
await updateJestConfigExt(tree);
|
||||
expect(tree.exists('jest.config.ts')).toBeTruthy();
|
||||
expect(tree.exists('jest.preset.js')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should NOT update jest.config.ts preset', async () => {
|
||||
await libSetUp(tree);
|
||||
|
||||
tree.rename('libs/lib-one/jest.config.js', 'libs/lib-one/jest.config.ts');
|
||||
const projectConfig = readProjectConfiguration(tree, 'lib-one');
|
||||
updateProjectConfiguration(tree, 'lib-one', {
|
||||
...projectConfig,
|
||||
targets: {
|
||||
test: {
|
||||
...projectConfig.targets.test,
|
||||
options: {
|
||||
jestConfig: 'libs/lib-one/jest.config.ts',
|
||||
passWithNoTests: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(tree.exists('libs/lib-one/jest.config.ts')).toBeTruthy();
|
||||
await updateJestConfigExt(tree);
|
||||
expect(tree.exists('libs/lib-one/jest.config.ts')).toBeTruthy();
|
||||
expect(tree.read('libs/lib-one/jest.config.ts', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should only update js/ts files', async () => {
|
||||
await libSetUp(tree);
|
||||
|
||||
tree.rename('libs/lib-one/jest.config.js', 'libs/lib-one/jest.config.ts');
|
||||
updateProjectConfiguration(tree, 'lib-one', {
|
||||
...readProjectConfiguration(tree, 'lib-one'),
|
||||
targets: {
|
||||
test: {
|
||||
executor: '@nrwl/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'libs/lib-one/jest.config.ts',
|
||||
passWithNoTests: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await libSetUp(tree, { ...setupDefaults, libName: 'lib-two' });
|
||||
tree.delete('libs/lib-two/jest.config.ts'); // lib generator creates a ts file
|
||||
tree.write('libs/lib-two/jest.config.json', '{}');
|
||||
updateProjectConfiguration(tree, 'lib-two', {
|
||||
...readProjectConfiguration(tree, 'lib-two'),
|
||||
targets: {
|
||||
test: {
|
||||
executor: '@nrwl/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'libs/lib-two/jest.config.json',
|
||||
passWithNoTests: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await libSetUp(tree, { ...setupDefaults, libName: 'lib-three' });
|
||||
expect(tree.exists('libs/lib-one/jest.config.ts')).toBeTruthy();
|
||||
await updateJestConfigExt(tree);
|
||||
expect(tree.exists('libs/lib-one/jest.config.ts')).toBeTruthy();
|
||||
expect(tree.exists('libs/lib-two/jest.config.ts')).toBeFalsy();
|
||||
expect(tree.exists('libs/lib-two/jest.config.json')).toBeTruthy();
|
||||
expect(tree.exists('libs/lib-three/jest.config.ts')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should not throw error if file does not exit', async () => {
|
||||
await libSetUp(tree);
|
||||
|
||||
tree.delete('libs/lib-one/jest.config.js');
|
||||
await updateJestConfigExt(tree);
|
||||
expect(tree.exists('libs/lib-one/jest.config.ts')).toBeFalsy();
|
||||
expect(tree.exists('libs/lib-one/jest.config.js')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should update correct tsconfigs', async () => {
|
||||
await libSetUp(tree);
|
||||
|
||||
updateJson(tree, 'libs/lib-one/tsconfig.lib.json', (json) => {
|
||||
json.exclude = ['src/**/*.spec.ts'];
|
||||
return json;
|
||||
});
|
||||
|
||||
updateJson(tree, 'libs/lib-one/tsconfig.spec.json', (json) => {
|
||||
json.include = ['src/**/*.spec.ts'];
|
||||
return json;
|
||||
});
|
||||
|
||||
await updateJestConfigExt(tree);
|
||||
|
||||
const tsconfig = readJson(tree, 'libs/lib-one/tsconfig.json');
|
||||
const libTsConfig = readJson(tree, 'libs/lib-one/tsconfig.lib.json');
|
||||
const specTsConfig = readJson(tree, 'libs/lib-one/tsconfig.spec.json');
|
||||
|
||||
expect(tsconfig.exclude).toBeFalsy();
|
||||
expect(libTsConfig.exclude).toEqual(['src/**/*.spec.ts', 'jest.config.ts']);
|
||||
expect(specTsConfig.exclude).toBeFalsy();
|
||||
expect(specTsConfig.include).toEqual([
|
||||
'src/**/*.spec.ts',
|
||||
'jest.config.ts',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should add exclude to root tsconfig with no references', async () => {
|
||||
await libSetUp(tree);
|
||||
|
||||
tree.delete('libs/lib-one/tsconfig.spec.json');
|
||||
tree.delete('libs/lib-one/tsconfig.lib.json');
|
||||
|
||||
updateJson(tree, 'libs/lib-one/tsconfig.json', (json) => {
|
||||
delete json.references;
|
||||
return json;
|
||||
});
|
||||
|
||||
await updateJestConfigExt(tree);
|
||||
|
||||
const tsconfig = readJson(tree, 'libs/lib-one/tsconfig.json');
|
||||
|
||||
expect(tsconfig.exclude).toEqual(['jest.config.ts']);
|
||||
expect(tree.exists('libs/lib-one/tsconfig.spec.json')).toBeFalsy();
|
||||
expect(tree.exists('libs/lib-one/tsconfig.lib.json')).toBeFalsy();
|
||||
});
|
||||
|
||||
it('should update the excludes of next js apps using the project parser settings', async () => {
|
||||
await libSetUp(tree, { ...setupDefaults, setParserOptionsProject: true });
|
||||
|
||||
const projectConfig = readProjectConfiguration(tree, 'lib-one');
|
||||
projectConfig.targets['build'] = {
|
||||
executor: '@nrwl/next:build',
|
||||
options: {},
|
||||
};
|
||||
updateProjectConfiguration(tree, 'lib-one', projectConfig);
|
||||
updateJson(tree, 'libs/lib-one/tsconfig.json', (json) => {
|
||||
// simulate nextJS tsconfig;
|
||||
json.exclude = ['node_modules'];
|
||||
return json;
|
||||
});
|
||||
const esLintJson = readJson(tree, 'libs/lib-one/.eslintrc.json');
|
||||
// make sure the parserOptions are set correctly
|
||||
expect(esLintJson.overrides[0]).toMatchSnapshot();
|
||||
|
||||
await updateJestConfigExt(tree);
|
||||
|
||||
const tsconfigSpec = readJson(tree, 'libs/lib-one/tsconfig.spec.json');
|
||||
expect(tsconfigSpec.exclude).toEqual(['node_modules']);
|
||||
});
|
||||
|
||||
it('should produce the same results when running multiple times', async () => {
|
||||
await libSetUp(tree);
|
||||
updateJson(tree, 'libs/lib-one/tsconfig.lib.json', (json) => {
|
||||
json.exclude = ['src/**/*.spec.ts'];
|
||||
return json;
|
||||
});
|
||||
updateJson(tree, 'libs/lib-one/tsconfig.spec.json', (json) => {
|
||||
json.include = ['src/**/*.spec.ts'];
|
||||
return json;
|
||||
});
|
||||
|
||||
await setupNextProj(tree);
|
||||
|
||||
const esLintJson = readJson(tree, 'libs/my-next-proj/.eslintrc.json');
|
||||
// make sure the parserOptions are set correctly for next
|
||||
expect(esLintJson.overrides[0]).toMatchSnapshot();
|
||||
|
||||
await updateJestConfigExt(tree);
|
||||
|
||||
assertNextProj(tree);
|
||||
assertLib(tree);
|
||||
|
||||
await updateJestConfigExt(tree);
|
||||
|
||||
assertNextProj(tree);
|
||||
assertLib(tree);
|
||||
|
||||
expect(tree.read('libs/lib-one/jest.config.ts', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
async function setupNextProj(tree: Tree) {
|
||||
await libSetUp(tree, {
|
||||
...setupDefaults,
|
||||
libName: 'my-next-proj',
|
||||
setParserOptionsProject: true,
|
||||
});
|
||||
const projectConfig = readProjectConfiguration(tree, 'my-next-proj');
|
||||
projectConfig.targets['build'] = {
|
||||
executor: '@nrwl/next:build',
|
||||
options: {},
|
||||
};
|
||||
updateProjectConfiguration(tree, 'my-next-proj', projectConfig);
|
||||
updateJson(tree, 'libs/my-next-proj/tsconfig.json', (json) => {
|
||||
// simulate nextJS tsconfig;
|
||||
json.exclude = ['node_modules'];
|
||||
return json;
|
||||
});
|
||||
}
|
||||
|
||||
function assertNextProj(tree: Tree) {
|
||||
expect(
|
||||
readJson(tree, 'libs/my-next-proj/tsconfig.spec.json').exclude
|
||||
).toEqual(['node_modules']);
|
||||
expect(
|
||||
readJson(tree, 'libs/my-next-proj/tsconfig.spec.json').include
|
||||
).toEqual(expect.arrayContaining(['jest.config.ts']));
|
||||
}
|
||||
|
||||
function assertLib(tree: Tree) {
|
||||
expect(readJson(tree, 'libs/lib-one/tsconfig.json').exclude).toBeFalsy();
|
||||
expect(readJson(tree, 'libs/lib-one/tsconfig.spec.json').exclude).toBeFalsy();
|
||||
expect(readJson(tree, 'libs/lib-one/tsconfig.spec.json').include).toEqual([
|
||||
'src/**/*.spec.ts',
|
||||
'jest.config.ts',
|
||||
]);
|
||||
|
||||
expect(readJson(tree, 'libs/lib-one/tsconfig.lib.json').exclude).toEqual([
|
||||
'src/**/*.spec.ts',
|
||||
'jest.config.ts',
|
||||
]);
|
||||
expect(readJson(tree, 'libs/lib-one/tsconfig.lib.json').include).toEqual([
|
||||
'src/**/*.ts',
|
||||
]);
|
||||
}
|
||||
@ -1,180 +0,0 @@
|
||||
import {
|
||||
formatFiles,
|
||||
getProjects,
|
||||
joinPathFragments,
|
||||
logger,
|
||||
ProjectConfiguration,
|
||||
readJson,
|
||||
stripIndents,
|
||||
Tree,
|
||||
updateJson,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { extname } from 'path';
|
||||
import { JestExecutorOptions } from '../../executors/jest/schema';
|
||||
|
||||
const allowedExt = ['.ts', '.js'];
|
||||
|
||||
function updateTsConfig(tree: Tree, tsConfigPath: string) {
|
||||
try {
|
||||
updateJson(
|
||||
tree,
|
||||
tsConfigPath,
|
||||
(json) => {
|
||||
json.exclude = Array.from(
|
||||
new Set([...(json.exclude || []), 'jest.config.ts'])
|
||||
);
|
||||
return json;
|
||||
},
|
||||
{
|
||||
allowTrailingComma: true,
|
||||
disallowComments: false,
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
logger.warn(
|
||||
stripIndents`Nx Unable to update ${tsConfigPath}. Please manually ignore the jest.config.ts file.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function addEsLintIgnoreComments(tree: Tree, filePath: string) {
|
||||
if (tree.exists(filePath)) {
|
||||
const contents = tree.read(filePath, 'utf-8');
|
||||
if (!contents.startsWith('/* eslint-disable */')) {
|
||||
tree.write(
|
||||
filePath,
|
||||
`/* eslint-disable */
|
||||
${contents}`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isJestConfigValid(tree: Tree, options: JestExecutorOptions) {
|
||||
const configExt = extname(options.jestConfig);
|
||||
|
||||
if (!tree.exists(options.jestConfig) || !allowedExt.includes(configExt)) {
|
||||
logger.debug(
|
||||
`unable to update file because it doesn't exist or is not a js or ts file. Config: ${
|
||||
options.jestConfig
|
||||
}. Exists?: ${tree.exists(options.jestConfig)}`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function updateTsconfigSpec(
|
||||
tree: Tree,
|
||||
projectConfig: ProjectConfiguration,
|
||||
path,
|
||||
options: { isNextWithProjectParse: boolean; tsConfigPath: string } = {
|
||||
isNextWithProjectParse: false,
|
||||
tsConfigPath: '',
|
||||
}
|
||||
) {
|
||||
updateJson(tree, joinPathFragments(projectConfig.root, path), (json) => {
|
||||
json.include = Array.from(
|
||||
new Set([...(json.include || []), 'jest.config.ts'])
|
||||
);
|
||||
if (options.isNextWithProjectParse) {
|
||||
const tsConfig = readJson(tree, options.tsConfigPath);
|
||||
const tsConfigExclude = (tsConfig.exclude || []).filter(
|
||||
(e) => e !== 'jest.config.ts'
|
||||
);
|
||||
json.exclude = Array.from(
|
||||
new Set([...(json.exclude || []), ...tsConfigExclude])
|
||||
);
|
||||
}
|
||||
return json;
|
||||
});
|
||||
}
|
||||
|
||||
function isNextWithProjectLint(
|
||||
projectConfig: ProjectConfiguration,
|
||||
esLintJson: any
|
||||
) {
|
||||
const esLintOverrides = esLintJson?.overrides?.find((o) =>
|
||||
['*.ts', '*.tsx', '*.js', '*.jsx'].every((ext) => o.files.includes(ext))
|
||||
);
|
||||
|
||||
// check if it's a next app and has a parserOptions.project set in the eslint overrides
|
||||
return !!(
|
||||
projectConfig?.targets?.['build']?.executor === '@nrwl/next:build' &&
|
||||
esLintOverrides?.parserOptions?.project
|
||||
);
|
||||
}
|
||||
|
||||
export async function updateJestConfigExt(tree: Tree) {
|
||||
if (tree.exists('jest.config.js')) {
|
||||
tree.rename('jest.config.js', 'jest.config.ts');
|
||||
}
|
||||
|
||||
const projects = getProjects(tree);
|
||||
|
||||
forEachExecutorOptions<JestExecutorOptions>(
|
||||
tree,
|
||||
'@nrwl/jest:jest',
|
||||
(options, projectName, target, configuration) => {
|
||||
const projectConfig = projects.get(projectName);
|
||||
|
||||
if (!options.jestConfig || !isJestConfigValid(tree, options)) {
|
||||
return;
|
||||
}
|
||||
|
||||
addEsLintIgnoreComments(tree, options.jestConfig);
|
||||
|
||||
const newJestConfigPath = options.jestConfig.replace('.js', '.ts');
|
||||
tree.rename(options.jestConfig, newJestConfigPath);
|
||||
|
||||
const rootFiles = tree.children(projectConfig.root);
|
||||
for (const fileName of rootFiles) {
|
||||
if (fileName === 'tsconfig.json') {
|
||||
const filePath = joinPathFragments(projectConfig.root, fileName);
|
||||
const tsConfig = readJson(tree, filePath, {
|
||||
allowTrailingComma: true,
|
||||
disallowComments: false,
|
||||
});
|
||||
|
||||
if (tsConfig.references) {
|
||||
for (const { path } of tsConfig.references) {
|
||||
// skip as editor.json should include everything anyway.
|
||||
if (path.endsWith('tsconfig.editor.json')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (path.endsWith('tsconfig.spec.json')) {
|
||||
const eslintPath = joinPathFragments(
|
||||
projectConfig.root,
|
||||
'.eslintrc.json'
|
||||
);
|
||||
updateTsconfigSpec(tree, projectConfig, path, {
|
||||
isNextWithProjectParse: tree.exists(eslintPath)
|
||||
? isNextWithProjectLint(
|
||||
projectConfig,
|
||||
readJson(tree, eslintPath)
|
||||
)
|
||||
: false,
|
||||
tsConfigPath: filePath,
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
updateTsConfig(tree, joinPathFragments(projectConfig.root, path));
|
||||
}
|
||||
} else {
|
||||
updateTsConfig(tree, filePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
projectConfig.targets[target].options.jestConfig = newJestConfigPath;
|
||||
updateProjectConfiguration(tree, projectName, projectConfig);
|
||||
}
|
||||
);
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
export default updateJestConfigExt;
|
||||
@ -1,76 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Jest Migration (v14.1.2) should convert module.exports => export default 1`] = `
|
||||
"const { getJestProjects } = require('@nrwl/jest');
|
||||
const nxPreset = require('@nrwl/jest/preset');
|
||||
|
||||
|
||||
const someFn = () => ({more: 'stuff'});
|
||||
module.export.abc = someFn;
|
||||
export default {
|
||||
...nxPreset,
|
||||
more: 'stuff',
|
||||
someFn,
|
||||
projects: getJestProjects()
|
||||
};"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration (v14.1.2) should update individual project jest configs 1`] = `
|
||||
"
|
||||
const nxPreset = require('@nrwl/jest/preset').default;
|
||||
const someOtherImport = require('../something/else.js');
|
||||
export default {
|
||||
...someOtherImport,
|
||||
...nxPreset,
|
||||
displayName: 'lib-one',
|
||||
preset: '../../jest.preset.js',
|
||||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
stringifyContentPathRegex: '\\\\.(html|svg)$',
|
||||
},
|
||||
},
|
||||
coverageDirectory: '../../coverage/apps/lib-one',
|
||||
transform: {
|
||||
'^.+\\\\.(ts|mjs|js|html)$': 'jest-preset-angular',
|
||||
},
|
||||
transformIgnorePatterns: ['node_modules/(?!.*\\\\.mjs$)'],
|
||||
snapshotSerializers: [
|
||||
'jest-preset-angular/build/serializers/no-ng-attributes',
|
||||
'jest-preset-angular/build/serializers/ng-snapshot',
|
||||
'jest-preset-angular/build/serializers/html-comment',
|
||||
],
|
||||
};
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`Jest Migration (v14.1.2) should work with multiple configurations 1`] = `
|
||||
"
|
||||
const nxPreset = require('@nrwl/jest/preset').default;
|
||||
const someOtherImport = require('../something/else.js');
|
||||
export default {
|
||||
...someOtherImport,
|
||||
...nxPreset,
|
||||
displayName: 'lib-one',
|
||||
preset: '../../jest.preset.js',
|
||||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
stringifyContentPathRegex: '\\\\.(html|svg)$',
|
||||
},
|
||||
},
|
||||
coverageDirectory: '../../coverage/apps/lib-one',
|
||||
transform: {
|
||||
'^.+\\\\.(ts|mjs|js|html)$': 'jest-preset-angular',
|
||||
},
|
||||
transformIgnorePatterns: ['node_modules/(?!.*\\\\.mjs$)'],
|
||||
snapshotSerializers: [
|
||||
'jest-preset-angular/build/serializers/no-ng-attributes',
|
||||
'jest-preset-angular/build/serializers/ng-snapshot',
|
||||
'jest-preset-angular/build/serializers/html-comment',
|
||||
],
|
||||
};
|
||||
"
|
||||
`;
|
||||
@ -1,201 +0,0 @@
|
||||
import {
|
||||
readProjectConfiguration,
|
||||
stripIndents,
|
||||
Tree,
|
||||
updateProjectConfiguration,
|
||||
updateJson,
|
||||
readJson,
|
||||
} from '@nx/devkit';
|
||||
import { createTree, createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { libraryGenerator as workspaceLib } from '@nx/js';
|
||||
import {
|
||||
updateExportsJestConfig,
|
||||
updateRootFiles,
|
||||
updateToDefaultExport,
|
||||
} from './update-exports-jest-config';
|
||||
|
||||
describe('Jest Migration (v14.1.2)', () => {
|
||||
let tree: Tree;
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
});
|
||||
|
||||
it('should update root jest files', () => {
|
||||
tree.write(
|
||||
'jest.config.ts',
|
||||
stripIndents`
|
||||
const { getJestProjects } = require('@nrwl/jest');
|
||||
|
||||
module.exports = {
|
||||
projects: getJestProjects()
|
||||
};`
|
||||
);
|
||||
|
||||
tree.write(
|
||||
'jest.preset.ts',
|
||||
stripIndents`
|
||||
const nxPreset = require('@nrwl/jest/preset');
|
||||
|
||||
module.exports = { ...nxPreset };`
|
||||
);
|
||||
|
||||
const status = updateRootFiles(tree);
|
||||
|
||||
expect(status).toEqual({ didUpdateRootPreset: true });
|
||||
expect(tree.read('jest.config.ts', 'utf-8')).toEqual(stripIndents`
|
||||
const { getJestProjects } = require('@nrwl/jest');
|
||||
|
||||
export default {
|
||||
projects: getJestProjects()
|
||||
};
|
||||
`);
|
||||
expect(tree.read('jest.preset.js', 'utf-8')).toEqual(stripIndents`
|
||||
const nxPreset = require('@nrwl/jest/preset').default;
|
||||
|
||||
module.exports = { ...nxPreset };`);
|
||||
});
|
||||
|
||||
it('should update individual project jest configs', async () => {
|
||||
await workspaceLib(tree, { name: 'lib-one' });
|
||||
const projectConfiguration = readProjectConfiguration(tree, 'lib-one');
|
||||
updateProjectConfiguration(tree, 'lib-one', {
|
||||
...projectConfiguration,
|
||||
targets: {
|
||||
...projectConfiguration.targets,
|
||||
test: {
|
||||
...projectConfiguration.targets.test,
|
||||
executor: '@nrwl/jest:jest',
|
||||
},
|
||||
},
|
||||
});
|
||||
tree.rename('jest.preset.js', 'jest.preset.ts');
|
||||
tree.write(
|
||||
'libs/lib-one/jest.config.ts',
|
||||
`
|
||||
const nxPreset = require('@nrwl/jest/preset');
|
||||
const someOtherImport = require('../something/else.js');
|
||||
module.exports = {
|
||||
...someOtherImport,
|
||||
...nxPreset,
|
||||
displayName: 'lib-one',
|
||||
preset: '../../jest.preset.ts',
|
||||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
stringifyContentPathRegex: '\\\\.(html|svg)$',
|
||||
},
|
||||
},
|
||||
coverageDirectory: '../../coverage/apps/lib-one',
|
||||
transform: {
|
||||
'^.+\\\\.(ts|mjs|js|html)$': 'jest-preset-angular',
|
||||
},
|
||||
transformIgnorePatterns: ['node_modules/(?!.*\\\\.mjs$)'],
|
||||
snapshotSerializers: [
|
||||
'jest-preset-angular/build/serializers/no-ng-attributes',
|
||||
'jest-preset-angular/build/serializers/ng-snapshot',
|
||||
'jest-preset-angular/build/serializers/html-comment',
|
||||
],
|
||||
};
|
||||
`
|
||||
);
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
delete json.devDependencies['ts-node'];
|
||||
return json;
|
||||
});
|
||||
expect(
|
||||
readJson(tree, 'package.json').devDependencies['ts-node']
|
||||
).toBeUndefined();
|
||||
updateExportsJestConfig(tree);
|
||||
|
||||
const config = tree.read('libs/lib-one/jest.config.ts', 'utf-8');
|
||||
expect(readJson(tree, 'package.json').devDependencies['ts-node']).toEqual(
|
||||
'10.9.1'
|
||||
);
|
||||
expect(config).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should work with multiple configurations', async () => {
|
||||
await workspaceLib(tree, { name: 'lib-one' });
|
||||
tree.rename('jest.preset.js', 'jest.preset.ts');
|
||||
updateProjectConfiguration(tree, 'lib-one', {
|
||||
...readProjectConfiguration(tree, 'lib-one'),
|
||||
targets: {
|
||||
test: {
|
||||
executor: '@nrwl/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'libs/lib-one/jest.config.ts',
|
||||
passWithoutTests: true,
|
||||
},
|
||||
configurations: {
|
||||
production: {
|
||||
silent: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
tree.write(
|
||||
'libs/lib-one/jest.config.ts',
|
||||
`
|
||||
const nxPreset = require('@nrwl/jest/preset');
|
||||
const someOtherImport = require('../something/else.js');
|
||||
module.exports = {
|
||||
...someOtherImport,
|
||||
...nxPreset,
|
||||
displayName: 'lib-one',
|
||||
preset: '../../jest.preset.ts',
|
||||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
stringifyContentPathRegex: '\\\\.(html|svg)$',
|
||||
},
|
||||
},
|
||||
coverageDirectory: '../../coverage/apps/lib-one',
|
||||
transform: {
|
||||
'^.+\\\\.(ts|mjs|js|html)$': 'jest-preset-angular',
|
||||
},
|
||||
transformIgnorePatterns: ['node_modules/(?!.*\\\\.mjs$)'],
|
||||
snapshotSerializers: [
|
||||
'jest-preset-angular/build/serializers/no-ng-attributes',
|
||||
'jest-preset-angular/build/serializers/ng-snapshot',
|
||||
'jest-preset-angular/build/serializers/html-comment',
|
||||
],
|
||||
};
|
||||
`
|
||||
);
|
||||
|
||||
updateExportsJestConfig(tree);
|
||||
|
||||
const config = tree.read('libs/lib-one/jest.config.ts', 'utf-8');
|
||||
expect(config).toMatchSnapshot();
|
||||
expect(tree.exists('jest.preset.ts')).toBeFalsy();
|
||||
expect(tree.exists('jest.preset.js')).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should convert module.exports => export default', () => {
|
||||
tree = createTree();
|
||||
|
||||
tree.write(
|
||||
'jest.config.js',
|
||||
stripIndents`
|
||||
const { getJestProjects } = require('@nrwl/jest');
|
||||
const nxPreset = require('@nrwl/jest/preset');
|
||||
|
||||
|
||||
const someFn = () => ({more: 'stuff'});
|
||||
module.export.abc = someFn;
|
||||
module.exports = {
|
||||
...nxPreset,
|
||||
more: 'stuff',
|
||||
someFn,
|
||||
projects: getJestProjects()
|
||||
};`
|
||||
);
|
||||
updateToDefaultExport(tree, 'jest.config.js');
|
||||
|
||||
expect(tree.read('jest.config.js', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@ -1,93 +0,0 @@
|
||||
import type { Tree } from '@nx/devkit';
|
||||
import { addDependenciesToPackageJson } from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { tsNodeVersion } from '../../utils/versions';
|
||||
import { tsquery } from '@phenomnomnominal/tsquery';
|
||||
import type { BinaryExpression } from 'typescript';
|
||||
import type { JestExecutorOptions } from '../../executors/jest/schema';
|
||||
|
||||
export function updateExportsJestConfig(tree: Tree) {
|
||||
const { didUpdateRootPreset } = updateRootFiles(tree);
|
||||
let shouldInstallTsNode = false;
|
||||
forEachExecutorOptions<JestExecutorOptions>(
|
||||
tree,
|
||||
'@nrwl/jest:jest',
|
||||
(options) => {
|
||||
if (options.jestConfig && tree.exists(options.jestConfig)) {
|
||||
if (options.jestConfig.endsWith('.ts')) {
|
||||
updateToDefaultExport(tree, options.jestConfig);
|
||||
shouldInstallTsNode = true;
|
||||
}
|
||||
|
||||
const updatedImport = updateNxPresetImport(
|
||||
tree.read(options.jestConfig, 'utf-8')
|
||||
);
|
||||
tree.write(options.jestConfig, updatedImport);
|
||||
|
||||
// jest.preset.ts => jest.preset.js
|
||||
if (didUpdateRootPreset) {
|
||||
const projectConfig = tree.read(options.jestConfig, 'utf-8');
|
||||
const updatedConfig = projectConfig.replace(
|
||||
/(preset:\s*['"][.\/]*)(jest\.preset\.ts)(['"])/g,
|
||||
'$1jest.preset.js$3'
|
||||
);
|
||||
tree.write(options.jestConfig, updatedConfig);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return shouldInstallTsNode
|
||||
? addDependenciesToPackageJson(tree, {}, { 'ts-node': tsNodeVersion })
|
||||
: () => {};
|
||||
}
|
||||
|
||||
export function updateRootFiles(tree: Tree): { didUpdateRootPreset: boolean } {
|
||||
let didUpdateRootPreset = false;
|
||||
if (tree.exists('jest.config.ts')) {
|
||||
updateToDefaultExport(tree, 'jest.config.ts');
|
||||
}
|
||||
|
||||
if (tree.exists('jest.preset.ts')) {
|
||||
// fix those who ran v14 migration where this was renamed.
|
||||
tree.rename('jest.preset.ts', 'jest.preset.js');
|
||||
didUpdateRootPreset = true;
|
||||
}
|
||||
|
||||
if (tree.exists('jest.preset.js')) {
|
||||
const newContents = updateNxPresetImport(
|
||||
tree.read('jest.preset.js', 'utf-8')
|
||||
);
|
||||
tree.write('jest.preset.js', newContents);
|
||||
}
|
||||
|
||||
return {
|
||||
didUpdateRootPreset,
|
||||
};
|
||||
}
|
||||
|
||||
function updateNxPresetImport(fileContents: string): string {
|
||||
return fileContents.replace(
|
||||
/require\(['"]@nrwl\/jest\/preset['"]\)[;\s]*?[\n\r]/g,
|
||||
`require('@nrwl/jest/preset').default;
|
||||
`
|
||||
);
|
||||
}
|
||||
|
||||
export function updateToDefaultExport(tree: Tree, filePath: string) {
|
||||
const newConfig = tsquery.replace(
|
||||
tree.read(filePath, 'utf-8'),
|
||||
'ExpressionStatement BinaryExpression',
|
||||
(node: BinaryExpression) => {
|
||||
if (node.left.getText() === 'module.exports') {
|
||||
return `export default ${node.right.getText()}`;
|
||||
}
|
||||
|
||||
return node.getText();
|
||||
}
|
||||
);
|
||||
|
||||
tree.write(filePath, newConfig);
|
||||
}
|
||||
|
||||
export default updateExportsJestConfig;
|
||||
@ -1,221 +0,0 @@
|
||||
import {
|
||||
readJson,
|
||||
readProjectConfiguration,
|
||||
updateJson,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { libraryGenerator as workspaceLib } from '@nx/js';
|
||||
import {
|
||||
checkDeps,
|
||||
updateConfigsJest28,
|
||||
updateJestConfig,
|
||||
} from './update-configs-jest-28';
|
||||
|
||||
const mockJestConfig = `
|
||||
import { nxPreset } from '@nrwl/jest/preset'
|
||||
const myGlobals = ['Math', 'Promise'];
|
||||
|
||||
export default {
|
||||
...nxPreset,
|
||||
displayName: 'test-ng-app',
|
||||
preset: '../../jest.preset.js',
|
||||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||
extraGlobals: ['Math', 'Something'],
|
||||
extraGlobals: [],
|
||||
extraGlobals: myGlobals,
|
||||
timers: 'fake',
|
||||
timers: 'modern',
|
||||
timers: 'legacy',
|
||||
timers: 'real',
|
||||
testURL: 'http://localhost',
|
||||
testURL: "123abc",
|
||||
testURL: \`BLAH\`,
|
||||
testEnvironment: 'jsdom',
|
||||
testRunner: 'jest-jasmine2',
|
||||
}
|
||||
`;
|
||||
describe('Jest Migration - jest 28 config support', () => {
|
||||
it('should update "extraGlobals" config option', () => {
|
||||
const actual = updateJestConfig(mockJestConfig);
|
||||
expect(actual).not.toContain(`extraGlobals`);
|
||||
expect(actual).toContain(`sandboxInjectedGlobals: ['Math', 'Something'],`);
|
||||
expect(actual).toContain(`sandboxInjectedGlobals: [],`);
|
||||
expect(actual).toContain(`sandboxInjectedGlobals: myGlobals,`);
|
||||
});
|
||||
|
||||
it('should update "testURL" config option', () => {
|
||||
const actual = updateJestConfig(mockJestConfig);
|
||||
expect(actual).not.toContain(`testURL`);
|
||||
expect(actual).toContain(
|
||||
`testEnvironmentOptions: {url: 'http://localhost'},`
|
||||
);
|
||||
expect(actual).toContain(`testEnvironmentOptions: {url: "123abc"},`);
|
||||
expect(actual).toContain(`testEnvironmentOptions: {url: \`BLAH\`},`);
|
||||
});
|
||||
|
||||
it('should update "timers" config option', () => {
|
||||
const actual = updateJestConfig(mockJestConfig);
|
||||
expect(actual).not.toContain(`timers`);
|
||||
expect(actual).toContain(`fakeTimers: { enableGlobally: false },`);
|
||||
expect(actual).toContain(`fakeTimers: { enableGlobally: true },`);
|
||||
expect(actual).toContain(`fakeTimers: { enableGlobally: true },`);
|
||||
expect(actual).toContain(
|
||||
`fakeTimers: { enableGlobally: true, legacyFakeTimers: true },`
|
||||
);
|
||||
});
|
||||
|
||||
it('should update jest-environment-jsdom if being used', async () => {
|
||||
let tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
tree.write(
|
||||
`package.json`,
|
||||
`{
|
||||
"name": "jest-28-test",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"jest": "^28.1.1",
|
||||
"jest-environment-jsdom": "^27.1.0",
|
||||
"jest-preset-angular": "^11.0.0",
|
||||
"nx": "14.1.6",
|
||||
"ts-jest": "^27.0.2",
|
||||
"ts-node": "9.1.1",
|
||||
"typescript": "~4.6.2"
|
||||
},
|
||||
"dependencies": {
|
||||
}
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
const actual = checkDeps(tree);
|
||||
expect(actual).toEqual({
|
||||
'jest-environment-jsdom': '28.1.1',
|
||||
});
|
||||
});
|
||||
|
||||
it('should update jest-jasmine2 if being used as a test runner', () => {
|
||||
let tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
tree.write(
|
||||
`package.json`,
|
||||
`{
|
||||
"name": "jest-28-test",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"jest": "^27.1.1",
|
||||
"jest-jasmine2": "^27.1.0",
|
||||
"nx": "14.1.6",
|
||||
"ts-jest": "^27.0.2",
|
||||
"ts-node": "9.1.1",
|
||||
"typescript": "~4.6.2"
|
||||
},
|
||||
"dependencies": {
|
||||
}
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
tree.write(
|
||||
'jest.preset.js',
|
||||
`
|
||||
const nxPreset = require('@nx/jest/preset').default;
|
||||
module.exports = {
|
||||
...nxPreset,
|
||||
testRunner: 'jest-jasmine2',
|
||||
};`
|
||||
);
|
||||
|
||||
const actual = checkDeps(tree);
|
||||
expect(actual).toEqual({
|
||||
'jest-jasmine2': '28.1.1',
|
||||
});
|
||||
});
|
||||
|
||||
it('should not install deps if they are not used', () => {
|
||||
let tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
tree.write(
|
||||
`package.json`,
|
||||
`{
|
||||
"name": "jest-28-test",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"jest": "^27.1.0",
|
||||
"nx": "14.1.6",
|
||||
"ts-jest": "^27.0.2",
|
||||
"ts-node": "9.1.1",
|
||||
"typescript": "~4.6.2"
|
||||
},
|
||||
"dependencies": {
|
||||
}
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
const actual = checkDeps(tree);
|
||||
expect(actual).toEqual({});
|
||||
});
|
||||
|
||||
it('should update deps from jest.config.ts', async () => {
|
||||
let tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
await workspaceLib(tree, { name: 'my-lib', unitTestRunner: 'jest' });
|
||||
const projectConfiguration = readProjectConfiguration(tree, 'my-lib');
|
||||
updateProjectConfiguration(tree, 'my-lib', {
|
||||
...projectConfiguration,
|
||||
targets: {
|
||||
...projectConfiguration.targets,
|
||||
test: {
|
||||
...projectConfiguration.targets.test,
|
||||
executor: '@nrwl/jest:jest',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
json.devDependencies['jest'] = '27.1.1';
|
||||
json.devDependencies['jest-environment-jsdom'] = '27.1.1';
|
||||
return json;
|
||||
});
|
||||
tree.write(
|
||||
'libs/my-lib/jest.config.ts',
|
||||
`
|
||||
export default {
|
||||
displayName: 'test-ng-app',
|
||||
preset: '../../jest.preset.js',
|
||||
setupFilesAfterEnv: ['<rootDir>/src/test-setup.ts'],
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
useESM: true,
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
stringifyContentPathRegex: '\\\\.(html|svg)$',
|
||||
},
|
||||
},
|
||||
testEnvironment: 'jsdom',
|
||||
testRunner: 'jest-jasmine2',
|
||||
coverageDirectory: '../../coverage/apps/test-ng-app',
|
||||
transform: {
|
||||
'^.+\\\\.(ts|mjs|js|html)$': 'jest-preset-angular',
|
||||
},
|
||||
transformIgnorePatterns: [
|
||||
'node_modules/(?!.*\\\\.mjs$|rxjs)',
|
||||
// 'node_modules/(?!rxjs)'
|
||||
],
|
||||
snapshotSerializers: [
|
||||
'jest-preset-angular/build/serializers/no-ng-attributes',
|
||||
'jest-preset-angular/build/serializers/ng-snapshot',
|
||||
'jest-preset-angular/build/serializers/html-comment',
|
||||
],
|
||||
}`
|
||||
);
|
||||
updateConfigsJest28(tree);
|
||||
|
||||
const packageJson = readJson(tree, 'package.json');
|
||||
expect(packageJson.devDependencies).toEqual(
|
||||
expect.objectContaining({
|
||||
'jest-environment-jsdom': '28.1.1',
|
||||
'jest-jasmine2': '28.1.1',
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -1,117 +0,0 @@
|
||||
import { addDependenciesToPackageJson, readJson, Tree } from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { tsquery } from '@phenomnomnominal/tsquery';
|
||||
import { isStringLiteralLike, PropertyAssignment } from 'typescript';
|
||||
import { JestExecutorOptions } from '../../executors/jest/schema';
|
||||
import {
|
||||
findRootJestConfig,
|
||||
findRootJestPreset,
|
||||
} from '../../utils/config/find-root-jest-files';
|
||||
|
||||
const jestVersion = '28.1.1';
|
||||
|
||||
const JASMINE_TEST_RUNNER = /(testRunner:\s*['"`])(jest-jasmine2)(['"`])/g;
|
||||
const JSDOM_TEST_ENV = /(testEnvironment:\s*['"`])(jsdom)(['"`])/g;
|
||||
|
||||
export function updateConfigsJest28(tree: Tree) {
|
||||
let devDeps = checkDeps(tree);
|
||||
forEachExecutorOptions<JestExecutorOptions>(
|
||||
tree,
|
||||
'@nrwl/jest:jest',
|
||||
(options) => {
|
||||
if (options.jestConfig && tree.exists(options.jestConfig)) {
|
||||
const updatedConfig = updateJestConfig(
|
||||
tree.read(options.jestConfig, 'utf-8')
|
||||
);
|
||||
|
||||
tree.write(options.jestConfig, updatedConfig);
|
||||
|
||||
const projectConfigCheck = testFileForDep(updatedConfig);
|
||||
devDeps = { ...devDeps, ...projectConfigCheck };
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return addDependenciesToPackageJson(tree, {}, devDeps);
|
||||
}
|
||||
|
||||
export function updateJestConfig(config: string): string {
|
||||
let content = tsquery.replace(
|
||||
config,
|
||||
'PropertyAssignment:has(Identifier[name="testURL"])',
|
||||
(node: PropertyAssignment) => {
|
||||
const value = node?.initializer?.getText();
|
||||
return `testEnvironmentOptions: {url: ${value}}`;
|
||||
}
|
||||
);
|
||||
|
||||
content = tsquery.replace(
|
||||
content,
|
||||
'PropertyAssignment > Identifier[name="extraGlobals"]',
|
||||
() => {
|
||||
return 'sandboxInjectedGlobals';
|
||||
}
|
||||
);
|
||||
|
||||
return tsquery.replace(
|
||||
content,
|
||||
'PropertyAssignment:has(Identifier[name="timers"])',
|
||||
(node: PropertyAssignment) => {
|
||||
// must guard against non string properties as that means it's already been manually migrated
|
||||
if (node?.initializer && isStringLiteralLike(node.initializer)) {
|
||||
const value = node?.initializer.getText().trim() as
|
||||
| 'fake'
|
||||
| 'modern'
|
||||
| 'real'
|
||||
| 'legacy';
|
||||
|
||||
// use .includes to ignore the different quotes (' " `)
|
||||
if (value.includes('fake') || value.includes('modern')) {
|
||||
return `fakeTimers: { enableGlobally: true }`;
|
||||
}
|
||||
if (value.includes('real')) {
|
||||
return `fakeTimers: { enableGlobally: false }`;
|
||||
}
|
||||
if (value.includes('legacy')) {
|
||||
return `fakeTimers: { enableGlobally: true, legacyFakeTimers: true }`;
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export function checkDeps(tree: Tree): Record<string, string> {
|
||||
const packageJson = readJson(tree, 'package.json');
|
||||
let devDeps = {};
|
||||
|
||||
if (packageJson.devDependencies['jest-preset-angular']) {
|
||||
devDeps['jest-environment-jsdom'] = jestVersion;
|
||||
}
|
||||
|
||||
const rootJestConfig = findRootJestConfig(tree);
|
||||
if (rootJestConfig) {
|
||||
const rootConfigCheck = testFileForDep(tree.read(rootJestConfig, 'utf-8'));
|
||||
devDeps = { ...devDeps, ...rootConfigCheck };
|
||||
}
|
||||
|
||||
const rootJestPreset = findRootJestPreset(tree);
|
||||
if (rootJestPreset) {
|
||||
const rootPresetCheck = testFileForDep(tree.read(rootJestPreset, 'utf-8'));
|
||||
devDeps = { ...devDeps, ...rootPresetCheck };
|
||||
}
|
||||
|
||||
return devDeps;
|
||||
}
|
||||
|
||||
function testFileForDep(config: string): Record<string, string> {
|
||||
const deps = {};
|
||||
if (JASMINE_TEST_RUNNER.test(config)) {
|
||||
deps['jest-jasmine2'] = jestVersion;
|
||||
}
|
||||
if (JSDOM_TEST_ENV.test(config)) {
|
||||
deps['jest-environment-jsdom'] = jestVersion;
|
||||
}
|
||||
return deps;
|
||||
}
|
||||
|
||||
export default updateConfigsJest28;
|
||||
@ -1,346 +0,0 @@
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { libraryGenerator as workspaceLib } from '@nx/js';
|
||||
import {
|
||||
readProjectConfiguration,
|
||||
updateProjectConfiguration,
|
||||
} from 'nx/src/generators/utils/project-configuration';
|
||||
import {
|
||||
updateJestFnMocks,
|
||||
updateJestImports,
|
||||
updateJestTimers,
|
||||
updateTestsJest28,
|
||||
} from './update-tests-jest-28';
|
||||
|
||||
describe('Jest Migration - jest 28 test files', () => {
|
||||
it('should convert test files', async () => {
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
await workspaceLib(tree, {
|
||||
name: 'blah',
|
||||
linter: undefined,
|
||||
unitTestRunner: 'jest',
|
||||
});
|
||||
const projectConfiguration = readProjectConfiguration(tree, 'blah');
|
||||
updateProjectConfiguration(tree, 'blah', {
|
||||
...projectConfiguration,
|
||||
targets: {
|
||||
...projectConfiguration.targets,
|
||||
test: {
|
||||
...projectConfiguration.targets.test,
|
||||
executor: '@nrwl/jest:jest',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await workspaceLib(tree, {
|
||||
name: 'blah-again',
|
||||
linter: undefined,
|
||||
unitTestRunner: 'jest',
|
||||
js: true,
|
||||
});
|
||||
const projectConfiguration2 = readProjectConfiguration(tree, 'blah-again');
|
||||
updateProjectConfiguration(tree, 'blah-again', {
|
||||
...projectConfiguration2,
|
||||
targets: {
|
||||
...projectConfiguration2.targets,
|
||||
test: {
|
||||
...projectConfiguration2.targets.test,
|
||||
executor: '@nrwl/jest:jest',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
tree.write(
|
||||
'libs/blah/src/lib/something/something.spec.ts',
|
||||
`
|
||||
import expect from 'expect';
|
||||
import { something } from './something';
|
||||
import { jest } from '@jest/globals';
|
||||
describe('my cool test', () => {
|
||||
it('should do something', () => {
|
||||
jest.useFakeTimers('modern')
|
||||
const mock = jest.fn<ReturnType<typeof something>, Parameters<typeof something>>();
|
||||
expect(something()).toBe(true)
|
||||
})
|
||||
it('should do something pt 2', () => {
|
||||
jest.useFakeTimers('legacy')
|
||||
const mock = jest.fn<boolean, MyType[]>();
|
||||
expect(something()).toBe(true)
|
||||
})
|
||||
it('should do something', () => {
|
||||
const mock = jest.fn<Promise<{}>, []>();
|
||||
expect(something()).toBe(true)
|
||||
})
|
||||
})`
|
||||
);
|
||||
|
||||
tree.write(
|
||||
'libs/blah/src/lib/something/another.spec.ts',
|
||||
`
|
||||
import expect from 'expect';
|
||||
import { something } from './something';
|
||||
describe('my cool test', () => {
|
||||
it('should do something', () => {
|
||||
jest.useFakeTimers('modern')
|
||||
const mock = jest.fn<ReturnType<typeof something>, Parameters<typeof something>>();
|
||||
expect(something()).toBe(true)
|
||||
})
|
||||
it('should do something pt 2', () => {
|
||||
jest.useFakeTimers('legacy')
|
||||
const mock = jest.fn<boolean, MyType[]>();
|
||||
expect(something()).toBe(true)
|
||||
})
|
||||
it('should do something', () => {
|
||||
const mock = jest.fn<Promise<{}>, []>();
|
||||
expect(something()).toBe(true)
|
||||
})
|
||||
})`
|
||||
);
|
||||
const pc = readProjectConfiguration(tree, 'blah');
|
||||
pc.targets['test'].configurations = {
|
||||
production: {
|
||||
...pc.targets['test'].options,
|
||||
ci: true,
|
||||
},
|
||||
};
|
||||
pc.targets['another-config'] = pc.targets['test'];
|
||||
updateProjectConfiguration(tree, 'blah', pc);
|
||||
|
||||
updateTestsJest28(tree);
|
||||
|
||||
expect(tree.read('libs/blah/src/lib/something/something.spec.ts', 'utf-8'))
|
||||
.toEqual(`
|
||||
import { expect } from 'expect';
|
||||
import { something } from './something';
|
||||
import { jest } from '@jest/globals';
|
||||
describe('my cool test', () => {
|
||||
it('should do something', () => {
|
||||
jest.useFakeTimers()
|
||||
const mock = jest.fn<typeof something>();
|
||||
expect(something()).toBe(true)
|
||||
})
|
||||
it('should do something pt 2', () => {
|
||||
jest.useFakeTimers({ legacyFakeTimers: true })
|
||||
const mock = jest.fn<() => boolean>();
|
||||
expect(something()).toBe(true)
|
||||
})
|
||||
it('should do something', () => {
|
||||
const mock = jest.fn<() => Promise<{}>>();
|
||||
expect(something()).toBe(true)
|
||||
})
|
||||
})`);
|
||||
expect(tree.read('libs/blah/src/lib/something/another.spec.ts', 'utf-8'))
|
||||
.toEqual(`
|
||||
import { expect } from 'expect';
|
||||
import { something } from './something';
|
||||
describe('my cool test', () => {
|
||||
it('should do something', () => {
|
||||
jest.useFakeTimers()
|
||||
const mock = jest.fn<ReturnType<typeof something>, Parameters<typeof something>>();
|
||||
expect(something()).toBe(true)
|
||||
})
|
||||
it('should do something pt 2', () => {
|
||||
jest.useFakeTimers({ legacyFakeTimers: true })
|
||||
const mock = jest.fn<boolean, MyType[]>();
|
||||
expect(something()).toBe(true)
|
||||
})
|
||||
it('should do something', () => {
|
||||
const mock = jest.fn<Promise<{}>, []>();
|
||||
expect(something()).toBe(true)
|
||||
})
|
||||
})`);
|
||||
});
|
||||
|
||||
it('should update ts-jest/utils to jest-mock', () => {
|
||||
// import { mocked } from 'ts-jest/utils' => import { mocked } from 'jest-mock';
|
||||
// const { mocked } = require('ts-jest/utils'); => const { mocked } = require('jest-mock');
|
||||
|
||||
const actual = updateJestImports(`
|
||||
import { mocked } from 'ts-jest/utils';
|
||||
import { somethingElse } from 'ts-jest/utils';
|
||||
const { mocked } = require('ts-jest/utils');
|
||||
const { somethingElse } = require('ts-jest/utils');
|
||||
import * from ts from 'typescript'
|
||||
const mockTs = mocked(ts);
|
||||
|
||||
describe('something expected', () => {
|
||||
it('should do something', () => {
|
||||
const actual = somethingExpected('abc');
|
||||
expect(1 + 1).toBe(2);
|
||||
});
|
||||
})
|
||||
`);
|
||||
|
||||
const expected = `
|
||||
import { mocked } from 'jest-mock';
|
||||
import { somethingElse } from 'ts-jest/utils';
|
||||
const { mocked } = require('jest-mock');
|
||||
const { somethingElse } = require('ts-jest/utils');
|
||||
import * from ts from 'typescript'
|
||||
const mockTs = mocked(ts);
|
||||
|
||||
describe('something expected', () => {
|
||||
it('should do something', () => {
|
||||
const actual = somethingExpected('abc');
|
||||
expect(1 + 1).toBe(2);
|
||||
});
|
||||
})
|
||||
`;
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
it('should update "expect" default import to named import', () => {
|
||||
// import expect from 'expect' => import { expect } from 'expect'
|
||||
// const expect = require('expect') => const { expect } = require('expect')
|
||||
const actual = updateJestImports(`
|
||||
import expect from 'expect';
|
||||
const expect = require('expect');
|
||||
const expect = require('something-else');
|
||||
import somethingExpected from 'my-expect';
|
||||
|
||||
describe('something expected', () => {
|
||||
it('should do something', () => {
|
||||
const actual = somethingExpected('abc');
|
||||
expect(1 + 1).toBe(2);
|
||||
});
|
||||
})
|
||||
`);
|
||||
|
||||
const expected = `
|
||||
import { expect } from 'expect';
|
||||
const { expect } = require('expect');
|
||||
const expect = require('something-else');
|
||||
import somethingExpected from 'my-expect';
|
||||
|
||||
describe('something expected', () => {
|
||||
it('should do something', () => {
|
||||
const actual = somethingExpected('abc');
|
||||
expect(1 + 1).toBe(2);
|
||||
});
|
||||
})
|
||||
`;
|
||||
expect(actual).toEqual(expected);
|
||||
});
|
||||
|
||||
it('should update jest.useFakeTimers() to new timer api', () => {
|
||||
const actual = updateJestTimers(
|
||||
`
|
||||
describe('some test', () => {
|
||||
it('should do something', () => {
|
||||
jest.useFakeTimers('modern')
|
||||
})
|
||||
|
||||
it('should do something else', () => {
|
||||
jest.useFakeTimers('legacy')
|
||||
})
|
||||
})
|
||||
`,
|
||||
false
|
||||
);
|
||||
|
||||
expect(actual).not.toContain("jest.useFakeTimers('modern')");
|
||||
expect(actual).not.toContain("jest.useFakeTimers('legacy')");
|
||||
expect(actual).toContain('jest.useFakeTimers()');
|
||||
expect(actual).toContain('jest.useFakeTimers({ legacyFakeTimers: true })');
|
||||
});
|
||||
|
||||
it('should update jest timers to new timer api w/legacy timers set in config', () => {
|
||||
const actual = updateJestTimers(
|
||||
`
|
||||
describe('some test', () => {
|
||||
it('should do something', () => {
|
||||
jest.useFakeTimers('modern')
|
||||
})
|
||||
|
||||
it('should do something else', () => {
|
||||
jest.useFakeTimers('legacy')
|
||||
})
|
||||
})
|
||||
`,
|
||||
true
|
||||
);
|
||||
// jest.useFakeTimers('modern') -> jest.useFakeTimers()
|
||||
// jest.useFakeTimers('legacy') -> jest.useFakeTimers({legacyFakeTimers: true})
|
||||
// if legacyFakeTimers is true in config, then
|
||||
// jest.useFakeTimers('modern') -> jest.useRealTimers({legacyFakeTimers: false})
|
||||
expect(actual).toEqual(`
|
||||
describe('some test', () => {
|
||||
it('should do something', () => {
|
||||
jest.useRealTimers({ legacyFakeTimers: false })
|
||||
})
|
||||
|
||||
it('should do something else', () => {
|
||||
jest.useFakeTimers({ legacyFakeTimers: true })
|
||||
})
|
||||
})
|
||||
`);
|
||||
});
|
||||
|
||||
it('should update jest.fn usage', () => {
|
||||
const actual = updateJestFnMocks(`
|
||||
import add from './add';
|
||||
describe('something', () => {
|
||||
it('should do something', () => {
|
||||
const noTypedMockAdd = jest.fn();
|
||||
})
|
||||
it('should do something', () => {
|
||||
const asyncMock = jest.fn<Promise<string>, []>()
|
||||
})
|
||||
it('should do something', () => {
|
||||
const mock = jest.fn<number, string[]>()
|
||||
})
|
||||
it('should do something', () => {
|
||||
const mockAdd = jest.fn<ReturnType<typeof add>, Parameters<typeof add>>();
|
||||
})
|
||||
})
|
||||
`);
|
||||
|
||||
expect(actual).not.toContain(
|
||||
'jest.fn<ReturnType<typeof add>, Parameters<typeof add>>()'
|
||||
);
|
||||
expect(actual).toContain('jest.fn<typeof add>()');
|
||||
expect(actual).not.toContain('jest.fn<number, []>()');
|
||||
expect(actual).toContain('jest.fn<() => number>()');
|
||||
expect(actual).not.toContain('jest.fn<Promise<string>, []>()');
|
||||
expect(actual).toContain('jest.fn<() => Promise<string>>()');
|
||||
});
|
||||
|
||||
it('should leave a TODO comment if it does not know how to upgrade', () => {
|
||||
const actual = updateJestFnMocks(`
|
||||
import add from './add';
|
||||
describe('something', () => {
|
||||
it('should do something', () => {
|
||||
const noTypedMockAdd = jest.fn<string, MyType>();
|
||||
})
|
||||
})
|
||||
`);
|
||||
|
||||
expect(actual).toContain(
|
||||
'/** TODO: Update jest.fn<T>() type args for Jest v28 https://jestjs.io/docs/upgrading-to-jest28#jestfn */ jest.fn<string, MyType>();'
|
||||
);
|
||||
});
|
||||
|
||||
it('should not touch already migrate jest.fn usage', () => {
|
||||
const original = `
|
||||
import add from './add';
|
||||
describe('something', () => {
|
||||
it('should do something', () => {
|
||||
const noTypedMockAdd = jest.fn();
|
||||
})
|
||||
it('should do something', () => {
|
||||
const asyncMock = jest.fn<() => number>>()
|
||||
})
|
||||
it('should do something', () => {
|
||||
const mock = jest.fn<() => Promise<string>>()
|
||||
})
|
||||
it('should do something', () => {
|
||||
const mockAdd = jest.fn<typeof add>();
|
||||
})
|
||||
})
|
||||
`;
|
||||
const actual = updateJestFnMocks(original);
|
||||
|
||||
expect(actual).toEqual(original);
|
||||
expect(actual).not.toContain(
|
||||
'/** TODO: Update jest.fn() types for Jest v28 https://jestjs.io/docs/upgrading-to-jest28#jestfn'
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -1,202 +0,0 @@
|
||||
import {
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
visitNotIgnoredFiles,
|
||||
} from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { tsquery } from '@phenomnomnominal/tsquery';
|
||||
import * as ts from 'typescript';
|
||||
import { JestExecutorOptions } from '../../executors/jest/schema';
|
||||
|
||||
export function updateTestsJest28(tree: Tree) {
|
||||
const testFilePatterns = /.*.(spec|test)\.(ts|js)x?/g;
|
||||
const legacyTimers =
|
||||
/(timers:\s*['"`]legacy['"`])|(legacyFakeTimers:\s*true)/g;
|
||||
forEachExecutorOptions<JestExecutorOptions>(
|
||||
tree,
|
||||
'@nrwl/jest:jest',
|
||||
(options, projectName) => {
|
||||
const projectConfig = readProjectConfiguration(tree, projectName);
|
||||
|
||||
const isUsingLegacyTimers =
|
||||
options.jestConfig &&
|
||||
tree.exists(options.jestConfig) &&
|
||||
legacyTimers.test(tree.read(options.jestConfig, 'utf-8'));
|
||||
|
||||
visitNotIgnoredFiles(tree, projectConfig.root, (filePath) => {
|
||||
if (!filePath.match(testFilePatterns)) {
|
||||
return;
|
||||
}
|
||||
let fileContent = tree.read(filePath, 'utf-8');
|
||||
fileContent = updateJestTimers(fileContent, isUsingLegacyTimers);
|
||||
|
||||
if (fileContent.includes('@jest/globals')) {
|
||||
fileContent = updateJestFnMocks(fileContent);
|
||||
}
|
||||
|
||||
fileContent = updateJestImports(fileContent);
|
||||
|
||||
tree.write(filePath, fileContent);
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* jest.useFakeTimers('modern') -> jest.useFakeTimers()
|
||||
* jest.useFakeTimers('legacy') -> jest.useFakeTimers({legacyFakeTimers: true})
|
||||
* if legacyFakeTimers is true in config, then
|
||||
* jest.useFakeTimers('modern') -> jest.useRealTimers({legacyFakeTimers: false})
|
||||
*/
|
||||
export function updateJestTimers(
|
||||
fileContents: string,
|
||||
legacyFakeTimersInConfig: boolean
|
||||
) {
|
||||
return tsquery.replace(
|
||||
fileContents,
|
||||
'CallExpression',
|
||||
(node: ts.StringLiteral) => {
|
||||
if (!node?.getText().startsWith('jest.useFakeTimers')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const timerType = node.getText();
|
||||
// will be modern or legacy with quotes
|
||||
// just make sure it's included to ignore different quote types
|
||||
if (timerType.includes('legacy')) {
|
||||
return 'jest.useFakeTimers({ legacyFakeTimers: true })';
|
||||
}
|
||||
if (legacyFakeTimersInConfig) {
|
||||
// using modern but have config set to legacy
|
||||
return 'jest.useRealTimers({ legacyFakeTimers: false })';
|
||||
}
|
||||
// have to include space otherwise empty string will not remove the string literal
|
||||
return 'jest.useFakeTimers()';
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* make sure using jest.fn<T>
|
||||
*/
|
||||
function isTypedJestFnMock(node: ts.CallExpression): boolean {
|
||||
return ts.isCallExpression(node) && node.getText().startsWith('jest.fn<');
|
||||
}
|
||||
|
||||
/**
|
||||
* has 2 args where the second is a tuple or array
|
||||
* i.e.
|
||||
* jest.fn<Promise<string>, []>()
|
||||
* jest.fn<number, MyType[]>()
|
||||
* jest.fn<number, [string, number, SomeType]>()
|
||||
*/
|
||||
function isValid2Args(node: ts.CallExpression): boolean {
|
||||
const r =
|
||||
node?.typeArguments.length === 2 &&
|
||||
(node.typeArguments[1]?.kind === ts.SyntaxKind.TupleType ||
|
||||
node.typeArguments[1]?.kind === ts.SyntaxKind.ArrayType);
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* has 1 arg where the type is NOT a FunctionType
|
||||
* if it's a function type then it's already using the correct syntax
|
||||
* i.e.
|
||||
* jest.fn<string>()
|
||||
* jest.fn<() => Promise<string>>() is already valid, don't change it.
|
||||
*/
|
||||
function isValid1Arg(node: ts.CallExpression): boolean {
|
||||
const r =
|
||||
node?.typeArguments.length === 1 &&
|
||||
node.typeArguments[0]?.kind !== ts.SyntaxKind.FunctionType &&
|
||||
node.typeArguments[0]?.kind !== ts.SyntaxKind.TypeQuery;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* has a type reference as a type args
|
||||
* jest.fn<ReturnType<typeof add>, Parameters<typeof add>>();
|
||||
*/
|
||||
function isValidTypeRef(node: ts.CallExpression): boolean {
|
||||
const r =
|
||||
node.typeArguments[0].kind === ts.SyntaxKind.TypeReference &&
|
||||
!!(node.typeArguments?.[0] as ts.TypeReferenceNode)?.typeArguments;
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* has valid type args. prevent converting an already converted jest.fn<T>()
|
||||
*/
|
||||
function isValidType(node: ts.CallExpression): boolean {
|
||||
const r =
|
||||
node?.typeArguments.length === 1 &&
|
||||
(node.typeArguments[0]?.kind === ts.SyntaxKind.FunctionType ||
|
||||
node.typeArguments[0]?.kind === ts.SyntaxKind.TypeReference ||
|
||||
node.typeArguments[0]?.kind === ts.SyntaxKind.TypeQuery ||
|
||||
node.parent.getText().includes('/** TODO:')); // has already been marked by a previous run.
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* this only applies to tests using @jest/globals
|
||||
* jest.fn<Promise<string>, []>() -> jest.fn<() => Promise<string>>()
|
||||
* jest.fn<number, string[]>() -> jest.fn<() => number>()
|
||||
* jest.fn<ReturnType<typeof add>, Parameters<typeof add>>(); -> jest.fn<typeof add>()
|
||||
*/
|
||||
export function updateJestFnMocks(fileContents: string): string {
|
||||
return tsquery.replace(
|
||||
fileContents,
|
||||
'CallExpression',
|
||||
(node: ts.CallExpression) => {
|
||||
if (!isTypedJestFnMock(node) || isValidType(node)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isValid2Args(node) || isValid1Arg(node)) {
|
||||
return `${
|
||||
node.getText().split('<')[0]
|
||||
}<() => ${node.typeArguments[0].getText()}>()`;
|
||||
}
|
||||
|
||||
if (isValidTypeRef(node)) {
|
||||
const innerType = (node.typeArguments[0] as ts.TypeReferenceNode)
|
||||
.typeArguments;
|
||||
return `${node.getText().split('<')[0]}<${innerType[0].getText()}>()`;
|
||||
}
|
||||
|
||||
return `/** TODO: Update jest.fn<T>() type args for Jest v28 https://jestjs.io/docs/upgrading-to-jest28#jestfn */ ${node.getText()}`;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* import expect from 'expect' -> import { expect } from 'expect'
|
||||
* const expect = require('expect') -> const { expect } = require('expect')
|
||||
* import { mocked } from 'ts-jest/utils' => import { mocked } from 'jest-mock';
|
||||
* const { mocked } = require('ts-jest/utils'); => const { mocked } = require('jest-mock');
|
||||
*/
|
||||
export function updateJestImports(content: string): string {
|
||||
const mockUpdatedImports = tsquery.replace(
|
||||
content,
|
||||
':matches(ImportDeclaration:has(Identifier[name="mocked"]) StringLiteral[value="ts-jest/utils"], VariableStatement:has(Identifier[name="mocked"]) StringLiteral[value="ts-jest/utils"])',
|
||||
() => {
|
||||
return "'jest-mock'";
|
||||
}
|
||||
);
|
||||
|
||||
return tsquery.replace(
|
||||
mockUpdatedImports,
|
||||
':matches(ImportDeclaration:has(StringLiteral[value="expect"]), VariableDeclaration:has(StringLiteral[value="expect"]))',
|
||||
(node: ts.ImportDeclaration | ts.VariableDeclaration) => {
|
||||
if (ts.isImportDeclaration(node)) {
|
||||
return `import { expect } from 'expect';`;
|
||||
}
|
||||
if (ts.isVariableDeclaration(node)) {
|
||||
return `{ expect } = require('expect')`; // this query doesn't capture the ; so we don't need to add it in the replace.
|
||||
}
|
||||
return;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default updateTestsJest28;
|
||||
@ -1,35 +1,5 @@
|
||||
{
|
||||
"generators": {
|
||||
"update-node-executor": {
|
||||
"cli": "nx",
|
||||
"version": "13.8.5-beta.1",
|
||||
"description": "Renames @nrwl/js:node to @nrwl/node:node",
|
||||
"factory": "./src/migrations/update-13-8-5/update-node-executor"
|
||||
},
|
||||
"update-swcrc": {
|
||||
"cli": "nx",
|
||||
"version": "13.8.5-beta.1",
|
||||
"description": "Adjust .swcrc to .lib.swcrc",
|
||||
"factory": "./src/migrations/update-13-8-5/update-swcrc"
|
||||
},
|
||||
"update-swcrc-exclude": {
|
||||
"cli": "nx",
|
||||
"version": "13.10.1-beta.1",
|
||||
"description": "Update .lib.swcrc to exclude missing test files",
|
||||
"factory": "./src/migrations/update-13-10-1/update-lib-swcrc-exclude"
|
||||
},
|
||||
"exclude-jest-config-swcrc": {
|
||||
"cli": "nx",
|
||||
"version": "14.0.0-beta.2",
|
||||
"description": "Exclude jest config from .lib.swcrc",
|
||||
"factory": "./src/migrations/update-14-0-0/exclude-jest-config-swcrc"
|
||||
},
|
||||
"update-swcrc-path": {
|
||||
"cli": "nx",
|
||||
"version": "14.1.5-beta.0",
|
||||
"description": "Rename option swcrcPath to swcrc, and resolve relative to workspace root",
|
||||
"factory": "./src/migrations/update-14-1-5/update-swcrc-path"
|
||||
},
|
||||
"rename-swcrc-config": {
|
||||
"cli": "nx",
|
||||
"version": "15.8.0-beta.0",
|
||||
|
||||
@ -1,36 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Update .lib.swcrc exclude should update the exclude pattern 1`] = `
|
||||
"{
|
||||
"jsc": {
|
||||
"target": "es2017",
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"decorators": true,
|
||||
"dynamicImport": true
|
||||
},
|
||||
"transform": {
|
||||
"decoratorMetadata": true,
|
||||
"legacyDecorator": true
|
||||
},
|
||||
"keepClassNames": true,
|
||||
"externalHelpers": true,
|
||||
"loose": true
|
||||
},
|
||||
"module": {
|
||||
"type": "commonjs",
|
||||
"strict": true,
|
||||
"noInterop": true
|
||||
},
|
||||
"sourceMaps": true,
|
||||
"exclude": [
|
||||
"jest.config.ts",
|
||||
".*\\\\.spec.tsx?$",
|
||||
".*\\\\.test.tsx?$",
|
||||
"./src/jest-setup.ts$",
|
||||
"./**/jest-setup.ts$",
|
||||
".*.js$"
|
||||
]
|
||||
}
|
||||
"
|
||||
`;
|
||||
@ -1,90 +0,0 @@
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
ProjectConfiguration,
|
||||
readJson,
|
||||
Tree,
|
||||
updateJson,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import updateSwcRcExclude from './update-lib-swcrc-exclude';
|
||||
|
||||
const projectConfig: ProjectConfiguration = {
|
||||
root: 'libs/swc-lib',
|
||||
sourceRoot: 'libs/swc-lib/src',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/js:swc',
|
||||
outputs: ['{options.outputPath}'],
|
||||
options: {
|
||||
outputPath: 'dist/libs/swc-lib',
|
||||
main: 'libs/swc-lib/src/index.ts',
|
||||
tsConfig: 'libs/swc-lib/tsconfig.lib.json',
|
||||
assets: ['libs/swc-lib/*.md'],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const oldSwcRc = {
|
||||
jsc: {
|
||||
target: 'es2017',
|
||||
parser: {
|
||||
syntax: 'typescript',
|
||||
decorators: true,
|
||||
dynamicImport: true,
|
||||
},
|
||||
transform: {
|
||||
decoratorMetadata: true,
|
||||
legacyDecorator: true,
|
||||
},
|
||||
keepClassNames: true,
|
||||
externalHelpers: true,
|
||||
loose: true,
|
||||
},
|
||||
module: {
|
||||
type: 'commonjs',
|
||||
strict: true,
|
||||
noInterop: true,
|
||||
},
|
||||
sourceMaps: true,
|
||||
exclude: [
|
||||
'./src/**/.*.spec.ts$',
|
||||
'./**/.*.spec.ts$',
|
||||
'./src/**/jest-setup.ts$',
|
||||
'./**/jest-setup.ts$',
|
||||
'./**/.*.js$',
|
||||
],
|
||||
};
|
||||
describe('Update .lib.swcrc exclude', () => {
|
||||
let tree: Tree;
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
addProjectConfiguration(tree, 'swc-lib', projectConfig);
|
||||
|
||||
tree.write('libs/swc-lib/.lib.swcrc', JSON.stringify(oldSwcRc));
|
||||
});
|
||||
|
||||
it('should update the exclude pattern', () => {
|
||||
updateSwcRcExclude(tree);
|
||||
expect(tree.read('libs/swc-lib/.lib.swcrc', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should NOT update the exclude pattern if not present', () => {
|
||||
updateJson(tree, 'libs/swc-lib/.lib.swcrc', (json) => {
|
||||
delete json.exclude;
|
||||
return json;
|
||||
});
|
||||
|
||||
const before = readJson(tree, 'libs/swc-lib/.lib.swcrc');
|
||||
updateSwcRcExclude(tree);
|
||||
const after = readJson(tree, 'libs/swc-lib/.lib.swcrc');
|
||||
|
||||
expect(after.exclude).toBeFalsy();
|
||||
expect(after).toEqual(before);
|
||||
});
|
||||
|
||||
it('should do nothing if .lib.swcrc doest not exist', () => {
|
||||
tree.delete('libs/swc-lib/.lib-swcrc');
|
||||
|
||||
expect(() => updateSwcRcExclude(tree)).not.toThrowError();
|
||||
});
|
||||
});
|
||||
@ -1,41 +0,0 @@
|
||||
import { readProjectConfiguration, Tree, updateJson } from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { join } from 'path';
|
||||
import { SwcExecutorOptions } from '../../utils/schema';
|
||||
import { defaultExclude } from '../../utils/swc/add-swc-config';
|
||||
|
||||
export default function updateSwcRcExclude(tree: Tree) {
|
||||
forEachExecutorOptions(
|
||||
tree,
|
||||
'@nrwl/js:swc',
|
||||
(config: SwcExecutorOptions, projectName) => {
|
||||
const projectConfig = readProjectConfiguration(tree, projectName);
|
||||
const libSwcPath = join(projectConfig.root, '.lib.swcrc');
|
||||
|
||||
if (!tree.exists(libSwcPath)) return;
|
||||
|
||||
updateJson(
|
||||
tree,
|
||||
libSwcPath,
|
||||
(json) => {
|
||||
if (json.exclude) {
|
||||
const excludePatterns = new Set([
|
||||
...defaultExclude,
|
||||
...json.exclude,
|
||||
]);
|
||||
// remove old patterns that are duplicate for new patterns
|
||||
// defined in defaultExclude
|
||||
excludePatterns.delete('./**/.*.spec.ts$');
|
||||
excludePatterns.delete('./src/**/.*.spec.ts$');
|
||||
excludePatterns.delete('./**/.*.js$');
|
||||
excludePatterns.delete('./src/**/jest-setup.ts$');
|
||||
|
||||
json.exclude = [...excludePatterns];
|
||||
}
|
||||
return json;
|
||||
},
|
||||
{ expectComments: true }
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
@ -1,59 +0,0 @@
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
readJson,
|
||||
readProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
|
||||
import update from './update-node-executor';
|
||||
|
||||
describe('Migration: rename execute to node', () => {
|
||||
it(`should rename the "execute" executor to "node"`, async () => {
|
||||
let tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
|
||||
addProjectConfiguration(tree, 'myapp', {
|
||||
root: 'apps/myapp',
|
||||
sourceRoot: 'apps/myapp/src',
|
||||
projectType: 'application',
|
||||
targets: {
|
||||
serve: {
|
||||
executor: '@nrwl/js:node',
|
||||
options: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const tasks = await update(tree);
|
||||
|
||||
expect(tasks).toBeDefined();
|
||||
expect(readProjectConfiguration(tree, 'myapp')).toEqual({
|
||||
$schema: '../../node_modules/nx/schemas/project-schema.json',
|
||||
name: 'myapp',
|
||||
root: 'apps/myapp',
|
||||
sourceRoot: 'apps/myapp/src',
|
||||
projectType: 'application',
|
||||
targets: {
|
||||
serve: {
|
||||
executor: '@nrwl/node:node',
|
||||
options: {},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it(`should skip migration if no projects use @nrwl/js:node`, async () => {
|
||||
let tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
|
||||
tree.write(
|
||||
'workspace.json',
|
||||
JSON.stringify({
|
||||
version: 2,
|
||||
projects: {},
|
||||
})
|
||||
);
|
||||
|
||||
const tasks = await update(tree);
|
||||
|
||||
expect(tasks).toBeUndefined();
|
||||
});
|
||||
});
|
||||
@ -1,36 +0,0 @@
|
||||
import {
|
||||
addDependenciesToPackageJson,
|
||||
formatFiles,
|
||||
getProjects,
|
||||
Tree,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { nxVersion } from '@nx/workspace/src/utils/versions';
|
||||
|
||||
export default async function update(host: Tree) {
|
||||
const projects = getProjects(host);
|
||||
let installNeeded = false;
|
||||
|
||||
for (const [name, config] of projects.entries()) {
|
||||
if (config?.targets?.serve?.executor !== '@nrwl/js:node') continue;
|
||||
|
||||
config.targets.serve.executor = '@nrwl/node:node';
|
||||
|
||||
installNeeded = true;
|
||||
updateProjectConfiguration(host, name, config);
|
||||
}
|
||||
|
||||
const task = installNeeded
|
||||
? addDependenciesToPackageJson(
|
||||
host,
|
||||
{},
|
||||
{
|
||||
'@nrwl/node': nxVersion,
|
||||
}
|
||||
)
|
||||
: undefined;
|
||||
|
||||
await formatFiles(host);
|
||||
|
||||
return task;
|
||||
}
|
||||
@ -1,95 +0,0 @@
|
||||
import {
|
||||
ProjectConfiguration,
|
||||
readJson,
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { libraryGenerator } from '../../generators/library/library';
|
||||
import { defaultExclude } from '../../utils/swc/add-swc-config';
|
||||
import update from './update-swcrc';
|
||||
|
||||
describe('Migration: adjust .swcrc', () => {
|
||||
let tree: Tree;
|
||||
let projectConfiguration: ProjectConfiguration;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
await libraryGenerator(tree, {
|
||||
name: 'swc',
|
||||
buildable: true,
|
||||
linter: 'none',
|
||||
unitTestRunner: 'none',
|
||||
});
|
||||
projectConfiguration = readProjectConfiguration(tree, 'swc');
|
||||
updateProjectConfiguration(tree, 'swc', {
|
||||
...projectConfiguration,
|
||||
targets: {
|
||||
...projectConfiguration.targets,
|
||||
build: {
|
||||
...projectConfiguration.targets['build'],
|
||||
executor: '@nrwl/js:swc',
|
||||
},
|
||||
},
|
||||
});
|
||||
// re-read the project configuration
|
||||
projectConfiguration = readProjectConfiguration(tree, 'swc');
|
||||
});
|
||||
|
||||
it('should rename .swcrc to .lib.swcrc', async () => {
|
||||
addSwcrc();
|
||||
await update(tree);
|
||||
|
||||
expect(tree.exists('libs/swc/.swcrc')).toEqual(false);
|
||||
expect(tree.exists('libs/swc/.lib.swcrc')).toEqual(true);
|
||||
});
|
||||
|
||||
it('should assign default exclude if swcrc does not already have exclude', async () => {
|
||||
addSwcrc();
|
||||
await update(tree);
|
||||
|
||||
expect(readJson(tree, 'libs/swc/.lib.swcrc')['exclude']).toEqual(
|
||||
defaultExclude
|
||||
);
|
||||
});
|
||||
|
||||
it('should use swcExclude (deprecated) to assign to exclude', async () => {
|
||||
const swcExclude = ['./src/**/.*.spec.ts$'];
|
||||
updateProjectConfiguration(tree, 'swc', {
|
||||
...projectConfiguration,
|
||||
targets: {
|
||||
...projectConfiguration.targets,
|
||||
build: {
|
||||
...projectConfiguration.targets['build'],
|
||||
options: {
|
||||
...projectConfiguration.targets['build']['options'],
|
||||
swcExclude,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
addSwcrc();
|
||||
await update(tree);
|
||||
|
||||
expect(readJson(tree, 'libs/swc/.lib.swcrc')['exclude']).toEqual(
|
||||
swcExclude
|
||||
);
|
||||
});
|
||||
|
||||
it('should skip updating "exclude" if swcrc already has "exclude" field', async () => {
|
||||
addSwcrc(true);
|
||||
await update(tree);
|
||||
expect(readJson(tree, 'libs/swc/.lib.swcrc')['exclude']).toEqual([
|
||||
'./**/.*.spec.ts$',
|
||||
]);
|
||||
});
|
||||
|
||||
function addSwcrc(withExclude = false) {
|
||||
tree.write(
|
||||
'libs/swc/.swcrc',
|
||||
JSON.stringify(withExclude ? { exclude: ['./**/.*.spec.ts$'] } : {})
|
||||
);
|
||||
}
|
||||
});
|
||||
@ -1,39 +0,0 @@
|
||||
import {
|
||||
formatFiles,
|
||||
getProjects,
|
||||
readJson,
|
||||
Tree,
|
||||
updateJson,
|
||||
} from '@nx/devkit';
|
||||
import { join } from 'path';
|
||||
import { defaultExclude } from '../../utils/swc/add-swc-config';
|
||||
|
||||
export default async function update(host: Tree) {
|
||||
const projects = getProjects(host);
|
||||
|
||||
for (const config of projects.values()) {
|
||||
if (config?.targets?.build?.executor !== '@nrwl/js:swc') continue;
|
||||
|
||||
const swcrcPath = join(config.root, '.swcrc');
|
||||
if (!host.exists(swcrcPath)) continue;
|
||||
|
||||
// rename
|
||||
const libSwcrcPath = join(config.root, '.lib.swcrc');
|
||||
host.rename(swcrcPath, libSwcrcPath);
|
||||
|
||||
const swcrcContent = readJson(host, libSwcrcPath);
|
||||
|
||||
// skip if swcrc already has "exclude" field
|
||||
if (swcrcContent['exclude']) continue;
|
||||
|
||||
// check swcExclude build options
|
||||
const exclude =
|
||||
config?.targets?.build?.options?.['swcExclude'] || defaultExclude;
|
||||
updateJson(host, libSwcrcPath, (swcrc) => {
|
||||
swcrc['exclude'] = exclude;
|
||||
return swcrc;
|
||||
});
|
||||
}
|
||||
|
||||
await formatFiles(host);
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`JS Migration (v14.0.0) should update the exclude pattern 1`] = `
|
||||
"{
|
||||
"jsc": {
|
||||
"target": "es2017",
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"decorators": true,
|
||||
"dynamicImport": true
|
||||
},
|
||||
"transform": {
|
||||
"decoratorMetadata": true,
|
||||
"legacyDecorator": true
|
||||
},
|
||||
"keepClassNames": true,
|
||||
"externalHelpers": true,
|
||||
"loose": true
|
||||
},
|
||||
"module": {
|
||||
"type": "commonjs",
|
||||
"strict": true,
|
||||
"noInterop": true
|
||||
},
|
||||
"sourceMaps": true,
|
||||
"exclude": [
|
||||
"jest.config.js",
|
||||
".*.spec.tsx?$",
|
||||
".*.test.tsx?$",
|
||||
"./src/jest-setup.ts$",
|
||||
"./**/jest-setup.ts$",
|
||||
".*.js$"
|
||||
]
|
||||
}
|
||||
"
|
||||
`;
|
||||
@ -1,89 +0,0 @@
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
readJson,
|
||||
Tree,
|
||||
updateJson,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { excludeJestConfigSwcrc } from './exclude-jest-config-swcrc';
|
||||
import { ProjectConfiguration } from 'nx/src/config/workspace-json-project-json';
|
||||
|
||||
const projectConfig: ProjectConfiguration = {
|
||||
root: 'libs/swc-lib',
|
||||
sourceRoot: 'libs/swc-lib/src',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/js:swc',
|
||||
outputs: ['{options.outputPath}'],
|
||||
options: {
|
||||
outputPath: 'dist/libs/swc-lib',
|
||||
main: 'libs/swc-lib/src/index.ts',
|
||||
tsConfig: 'libs/swc-lib/tsconfig.lib.json',
|
||||
assets: ['libs/swc-lib/*.md'],
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const oldSwcRc = {
|
||||
jsc: {
|
||||
target: 'es2017',
|
||||
parser: {
|
||||
syntax: 'typescript',
|
||||
decorators: true,
|
||||
dynamicImport: true,
|
||||
},
|
||||
transform: {
|
||||
decoratorMetadata: true,
|
||||
legacyDecorator: true,
|
||||
},
|
||||
keepClassNames: true,
|
||||
externalHelpers: true,
|
||||
loose: true,
|
||||
},
|
||||
module: {
|
||||
type: 'commonjs',
|
||||
strict: true,
|
||||
noInterop: true,
|
||||
},
|
||||
sourceMaps: true,
|
||||
exclude: [
|
||||
'.*.spec.tsx?$',
|
||||
'.*.test.tsx?$',
|
||||
'./src/jest-setup.ts$',
|
||||
'./**/jest-setup.ts$',
|
||||
'.*.js$',
|
||||
],
|
||||
};
|
||||
describe('JS Migration (v14.0.0)', () => {
|
||||
let tree: Tree;
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
addProjectConfiguration(tree, 'swc-lib', projectConfig);
|
||||
|
||||
tree.write('libs/swc-lib/.lib.swcrc', JSON.stringify(oldSwcRc));
|
||||
});
|
||||
|
||||
it('should update the exclude pattern', () => {
|
||||
excludeJestConfigSwcrc(tree);
|
||||
expect(tree.read('libs/swc-lib/.lib.swcrc', 'utf-8')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should NOT update the exclude pattern if not present', () => {
|
||||
updateJson(tree, 'libs/swc-lib/.lib.swcrc', (json) => {
|
||||
delete json.exclude;
|
||||
return json;
|
||||
});
|
||||
|
||||
const before = readJson(tree, 'libs/swc-lib/.lib.swcrc');
|
||||
excludeJestConfigSwcrc(tree);
|
||||
const after = readJson(tree, 'libs/swc-lib/.lib.swcrc');
|
||||
|
||||
expect(after.exclude).toBeFalsy();
|
||||
expect(after).toEqual(before);
|
||||
});
|
||||
|
||||
it('should do nothing if .lib.swcrc doest not exist', () => {
|
||||
tree.delete('libs/swc-lib/.lib-swcrc');
|
||||
expect(() => excludeJestConfigSwcrc(tree)).not.toThrowError();
|
||||
});
|
||||
});
|
||||
@ -1,35 +0,0 @@
|
||||
import { readProjectConfiguration, Tree, updateJson } from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { SwcExecutorOptions } from '../../utils/schema';
|
||||
import { join } from 'path';
|
||||
|
||||
export function excludeJestConfigSwcrc(tree: Tree) {
|
||||
forEachExecutorOptions(
|
||||
tree,
|
||||
'@nrwl/js:swc',
|
||||
(config: SwcExecutorOptions, projectName) => {
|
||||
const projectConfig = readProjectConfiguration(tree, projectName);
|
||||
const libSwcPath = join(projectConfig.root, '.lib.swcrc');
|
||||
|
||||
if (!tree.exists(libSwcPath)) return;
|
||||
|
||||
updateJson(
|
||||
tree,
|
||||
libSwcPath,
|
||||
(json) => {
|
||||
if (json.exclude) {
|
||||
const excludePatterns = new Set([
|
||||
'jest.config.js',
|
||||
...json.exclude,
|
||||
]);
|
||||
json.exclude = [...excludePatterns];
|
||||
}
|
||||
return json;
|
||||
},
|
||||
{ expectComments: true }
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
export default excludeJestConfigSwcrc;
|
||||
@ -1,31 +0,0 @@
|
||||
import { addProjectConfiguration, readProjectConfiguration } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import updateSwcrcPath from './update-swcrc-path';
|
||||
|
||||
describe('update-swcrc-path migration', () => {
|
||||
it('should replace relative `swcrcPath` option with absolute `swcrc`', async () => {
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
addProjectConfiguration(tree, 'test-package', {
|
||||
root: 'packages/test-package',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/js:swc',
|
||||
options: {
|
||||
swcrcPath: 'config/swcrc.json',
|
||||
somethingThatShouldNotBeRemoved: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await updateSwcrcPath(tree);
|
||||
|
||||
const { targets, root } = readProjectConfiguration(tree, 'test-package');
|
||||
expect(root).toBe('packages/test-package');
|
||||
expect(targets.build.options.somethingThatShouldNotBeRemoved).toBeDefined();
|
||||
expect(targets.build.options.swcrcPath).toBeUndefined();
|
||||
expect(targets.build.options.swcrc).toBe(
|
||||
'packages/test-package/config/swcrc.json'
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -1,46 +0,0 @@
|
||||
import {
|
||||
formatFiles,
|
||||
joinPathFragments,
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
|
||||
import { SwcExecutorOptions } from '../../utils/schema';
|
||||
|
||||
type OldSwcExecutorOptions = SwcExecutorOptions & { swcrcPath?: string };
|
||||
|
||||
export async function updateSwcrcPath(tree: Tree) {
|
||||
let changesMade = false;
|
||||
|
||||
forEachExecutorOptions(
|
||||
tree,
|
||||
'@nrwl/js:swc',
|
||||
(_, projectName, targetName, configurationName) => {
|
||||
const projectConfig = readProjectConfiguration(tree, projectName);
|
||||
const executorOptions: OldSwcExecutorOptions = configurationName
|
||||
? projectConfig.targets[targetName].configurations[configurationName]
|
||||
: projectConfig.targets[targetName].options;
|
||||
|
||||
if (!executorOptions.swcrcPath) return;
|
||||
|
||||
const newSwcrcPath = joinPathFragments(
|
||||
projectConfig.root,
|
||||
executorOptions.swcrcPath
|
||||
);
|
||||
|
||||
delete executorOptions.swcrcPath;
|
||||
executorOptions.swcrc = newSwcrcPath;
|
||||
|
||||
updateProjectConfiguration(tree, projectName, projectConfig);
|
||||
|
||||
changesMade = true;
|
||||
}
|
||||
);
|
||||
|
||||
if (changesMade) {
|
||||
await formatFiles(tree);
|
||||
}
|
||||
}
|
||||
|
||||
export default updateSwcrcPath;
|
||||
@ -1,53 +1,5 @@
|
||||
{
|
||||
"generators": {
|
||||
"remove-eslint-project-config-if-no-type-checking-rules": {
|
||||
"cli": "nx",
|
||||
"version": "12.4.0-beta.0",
|
||||
"description": "Remove ESLint parserOptions.project config if no rules requiring type-checking are in use",
|
||||
"factory": "./src/migrations/update-12-4-0/remove-eslint-project-config-if-no-type-checking-rules"
|
||||
},
|
||||
"add-outputs": {
|
||||
"cli": "nx",
|
||||
"version": "12.9.0-beta.0",
|
||||
"description": "Add outputs for caching",
|
||||
"factory": "./src/migrations/update-12-9-0/add-outputs"
|
||||
},
|
||||
"remove-eslint-project-config-if-no-type-checking-rules-again": {
|
||||
"cli": "nx",
|
||||
"version": "12.9.0-beta.0",
|
||||
"description": "Remove ESLint parserOptions.project config if no rules requiring type-checking are in use",
|
||||
"factory": "./src/migrations/update-12-4-0/remove-eslint-project-config-if-no-type-checking-rules"
|
||||
},
|
||||
"eslint-8-updates": {
|
||||
"cli": "nx",
|
||||
"version": "13.3.0-beta.0",
|
||||
"description": "Update eslint-rules jest.config.js in order to support ESLint v8 exports mapping, remove category field",
|
||||
"factory": "./src/migrations/update-13-3-0/eslint-8-updates"
|
||||
},
|
||||
"add-swc-deps": {
|
||||
"cli": "nx",
|
||||
"version": "14.1.9-beta.0",
|
||||
"description": "Adds @swc/core and @swc-node as a dev dep if you are using them",
|
||||
"factory": "./src/migrations/update-14-1-9/add-swc-deps-if-needed"
|
||||
},
|
||||
"add-swc-deps-again": {
|
||||
"cli": "nx",
|
||||
"version": "14.2.3-beta.0",
|
||||
"description": "Adds @swc/core and @swc-node as a dev dep if you are using them (repeated due to prior mistake)",
|
||||
"factory": "./src/migrations/update-14-1-9/add-swc-deps-if-needed"
|
||||
},
|
||||
"experimental-to-utils-deps": {
|
||||
"cli": "nx",
|
||||
"version": "14.4.4",
|
||||
"description": "Adds @typescript-eslint/utils as a dev dep",
|
||||
"factory": "./src/migrations/update-14-4-4/experimental-to-utils-deps"
|
||||
},
|
||||
"experimental-to-utils-rules": {
|
||||
"cli": "nx",
|
||||
"version": "14.4.4",
|
||||
"description": "Switch from @typescript-eslint/experimental-utils to @typescript-eslint/utils in all rules and rules.spec files",
|
||||
"factory": "./src/migrations/update-14-4-4/experimental-to-utils-rules"
|
||||
},
|
||||
"add-eslint-inputs": {
|
||||
"cli": "nx",
|
||||
"version": "15.0.0-beta.0",
|
||||
@ -73,166 +25,6 @@
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
"12.6.0": {
|
||||
"version": "12.6.0-beta.8",
|
||||
"packages": {
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "~4.28.0"
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "~4.28.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"12.10.0": {
|
||||
"version": "12.10.0-beta.1",
|
||||
"packages": {
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "~4.31.1"
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "~4.31.1"
|
||||
},
|
||||
"@typescript-eslint/experimental-utils": {
|
||||
"version": "~4.31.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"13.0.0": {
|
||||
"version": "12.10.0",
|
||||
"packages": {
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "~4.33.0"
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "~4.33.0"
|
||||
},
|
||||
"@typescript-eslint/experimental-utils": {
|
||||
"version": "~4.33.0"
|
||||
},
|
||||
"eslint": {
|
||||
"version": "7.32.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"13.3.0": {
|
||||
"version": "13.3.0-beta.0",
|
||||
"packages": {
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "~5.3.0"
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "~5.3.0"
|
||||
},
|
||||
"@typescript-eslint/experimental-utils": {
|
||||
"version": "~5.3.0"
|
||||
},
|
||||
"eslint": {
|
||||
"version": "8.2.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"13.7.0": {
|
||||
"version": "13.7.0-beta.0",
|
||||
"packages": {
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "~5.10.0"
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "~5.10.0"
|
||||
},
|
||||
"@typescript-eslint/experimental-utils": {
|
||||
"version": "~5.10.0"
|
||||
},
|
||||
"eslint": {
|
||||
"version": "~8.7.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"13.10.0": {
|
||||
"version": "13.10.0-beta.0",
|
||||
"packages": {
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "~5.18.0"
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "~5.18.0"
|
||||
},
|
||||
"@typescript-eslint/experimental-utils": {
|
||||
"version": "~5.18.0"
|
||||
},
|
||||
"eslint": {
|
||||
"version": "~8.12.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.2.0": {
|
||||
"version": "14.2.0-beta.0",
|
||||
"packages": {
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "~5.24.0"
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "~5.24.0"
|
||||
},
|
||||
"@typescript-eslint/experimental-utils": {
|
||||
"version": "~5.24.0"
|
||||
},
|
||||
"eslint": {
|
||||
"version": "~8.15.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.3.7": {
|
||||
"version": "14.3.6",
|
||||
"packages": {
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "^5.29.0"
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "^5.29.0"
|
||||
},
|
||||
"@typescript-eslint/experimental-utils": {
|
||||
"version": "^5.29.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.4.4": {
|
||||
"version": "14.4.4",
|
||||
"packages": {
|
||||
"@typescript-eslint/utils": {
|
||||
"version": "^5.29.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.6.0": {
|
||||
"version": "14.6.0-rc.2",
|
||||
"packages": {
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "~5.33.1"
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "~5.33.1"
|
||||
},
|
||||
"@typescript-eslint/utils": {
|
||||
"version": "~5.33.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.8.0": {
|
||||
"version": "14.8.0-beta.0",
|
||||
"packages": {
|
||||
"@typescript-eslint/parser": {
|
||||
"version": "^5.36.1"
|
||||
},
|
||||
"@typescript-eslint/eslint-plugin": {
|
||||
"version": "^5.36.1"
|
||||
},
|
||||
"@typescript-eslint/utils": {
|
||||
"version": "^5.36.1"
|
||||
}
|
||||
}
|
||||
},
|
||||
"16.0.0": {
|
||||
"version": "16.0.0-beta.0",
|
||||
"packages": {
|
||||
|
||||
@ -33,7 +33,6 @@
|
||||
"eslint": "^8.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"@phenomnomnominal/tsquery": "~5.0.1",
|
||||
"tslib": "^2.3.0",
|
||||
"@nx/devkit": "file:../devkit",
|
||||
"@nx/js": "file:../js",
|
||||
|
||||
@ -1,231 +0,0 @@
|
||||
import { addProjectConfiguration, readJson, Tree, writeJson } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import removeESLintProjectConfigIfNoTypeCheckingRules from './remove-eslint-project-config-if-no-type-checking-rules';
|
||||
import type { Linter } from 'eslint';
|
||||
const KNOWN_RULE_REQUIRING_TYPE_CHECKING = '@typescript-eslint/await-thenable';
|
||||
|
||||
describe('Remove ESLint parserOptions.project config if no rules requiring type-checking are in use', () => {
|
||||
let tree: Tree;
|
||||
beforeEach(async () => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
addProjectConfiguration(tree, 'react-app', {
|
||||
root: 'apps/react-app',
|
||||
sourceRoot: 'apps/react-app/src',
|
||||
projectType: 'application',
|
||||
targets: {},
|
||||
});
|
||||
addProjectConfiguration(tree, 'workspace-lib', {
|
||||
root: 'libs/workspace-lib',
|
||||
sourceRoot: 'libs/workspace-lib/src',
|
||||
projectType: 'library',
|
||||
targets: {},
|
||||
});
|
||||
addProjectConfiguration(tree, 'some-lib', {
|
||||
root: 'libs/some-lib',
|
||||
sourceRoot: 'libs/some-lib/src',
|
||||
projectType: 'library',
|
||||
targets: {},
|
||||
});
|
||||
addProjectConfiguration(tree, 'another-lib', {
|
||||
root: 'libs/another-lib',
|
||||
sourceRoot: 'libs/another-lib/src',
|
||||
projectType: 'library',
|
||||
targets: {},
|
||||
});
|
||||
});
|
||||
|
||||
it('should not update any configs if the root .eslintrc.json contains at least one rule requiring type-checking', async () => {
|
||||
const rootEslintConfig = {
|
||||
root: true,
|
||||
ignorePatterns: ['**/*'],
|
||||
plugins: ['@nrwl/nx'],
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
|
||||
rules: {
|
||||
'@nrwl/nx/enforce-module-boundaries': [
|
||||
'error',
|
||||
{
|
||||
enforceBuildableLibDependency: true,
|
||||
allow: [],
|
||||
depConstraints: [
|
||||
{ sourceTag: '*', onlyDependOnLibsWithTags: ['*'] },
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
extends: ['plugin:@nrwl/nx/typescript'],
|
||||
rules: {
|
||||
[KNOWN_RULE_REQUIRING_TYPE_CHECKING]: 'error',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['*.js', '*.jsx'],
|
||||
extends: ['plugin:@nrwl/nx/javascript'],
|
||||
rules: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
writeJson(tree, '.eslintrc.json', rootEslintConfig);
|
||||
|
||||
const projectEslintConfig1 = {
|
||||
extends: '../../../.eslintrc.json',
|
||||
ignorePatterns: ['!**/*'],
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
parserOptions: {
|
||||
project: 'some-path-to-tsconfig.json',
|
||||
},
|
||||
rules: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
writeJson(tree, 'apps/react-app/.eslintrc.json', projectEslintConfig1);
|
||||
|
||||
const projectEslintConfig2 = {
|
||||
extends: '../../../.eslintrc.json',
|
||||
ignorePatterns: ['!**/*'],
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
parserOptions: {
|
||||
project: 'some-path-to-tsconfig.json',
|
||||
},
|
||||
rules: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
writeJson(tree, 'libs/workspace-lib/.eslintrc.json', projectEslintConfig2);
|
||||
|
||||
await removeESLintProjectConfigIfNoTypeCheckingRules(tree);
|
||||
|
||||
// No change
|
||||
expect(readJson(tree, 'apps/react-app/.eslintrc.json')).toEqual(
|
||||
projectEslintConfig1
|
||||
);
|
||||
|
||||
// No change
|
||||
expect(readJson(tree, 'libs/workspace-lib/.eslintrc.json')).toEqual(
|
||||
projectEslintConfig1
|
||||
);
|
||||
});
|
||||
|
||||
it('should remove the parserOptions.project from any project .eslintrc.json files that do not contain any rules requiring type-checking', async () => {
|
||||
// Root doesn't contain any rules requiring type-checking
|
||||
const rootEslintConfig = {
|
||||
root: true,
|
||||
ignorePatterns: ['**/*'],
|
||||
plugins: ['@nrwl/nx'],
|
||||
overrides: [],
|
||||
};
|
||||
writeJson(tree, '.eslintrc.json', rootEslintConfig);
|
||||
|
||||
const projectEslintConfig1: Linter.Config = {
|
||||
extends: '../../../.eslintrc.json',
|
||||
ignorePatterns: ['!**/*'],
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
parserOptions: {
|
||||
project: 'some-path-to-tsconfig.json',
|
||||
},
|
||||
rules: {
|
||||
[KNOWN_RULE_REQUIRING_TYPE_CHECKING]: 'error',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
writeJson(tree, 'apps/react-app/.eslintrc.json', projectEslintConfig1);
|
||||
|
||||
const projectEslintConfig2: Linter.Config = {
|
||||
extends: '../../../.eslintrc.json',
|
||||
ignorePatterns: ['!**/*'],
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
parserOptions: {
|
||||
project: 'some-path-to-tsconfig.json',
|
||||
},
|
||||
rules: {
|
||||
// No rules requiring type-checking
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
writeJson(tree, 'libs/workspace-lib/.eslintrc.json', projectEslintConfig2);
|
||||
|
||||
const projectEslintConfig3: Linter.Config = {
|
||||
extends: '../../../.eslintrc.json',
|
||||
ignorePatterns: ['!**/*'],
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.ts', '*.tsx'],
|
||||
parserOptions: {
|
||||
project: 'some-path-to-tsconfig.json',
|
||||
},
|
||||
rules: {
|
||||
[KNOWN_RULE_REQUIRING_TYPE_CHECKING]: 'off',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
writeJson(tree, 'libs/another-lib/.eslintrc.json', projectEslintConfig3);
|
||||
|
||||
await removeESLintProjectConfigIfNoTypeCheckingRules(tree);
|
||||
|
||||
// No change - uses rule requiring type-checking
|
||||
expect(readJson(tree, 'apps/react-app/.eslintrc.json')).toEqual(
|
||||
projectEslintConfig1
|
||||
);
|
||||
|
||||
// Updated - no more parserOptions.project
|
||||
expect(readJson(tree, 'libs/workspace-lib/.eslintrc.json'))
|
||||
.toMatchInlineSnapshot(`
|
||||
{
|
||||
"extends": "../../../.eslintrc.json",
|
||||
"ignorePatterns": [
|
||||
"!**/*",
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"*.ts",
|
||||
"*.tsx",
|
||||
],
|
||||
"rules": {},
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
|
||||
// Updated - no more parserOptions.project
|
||||
expect(readJson(tree, 'libs/another-lib/.eslintrc.json'))
|
||||
.toMatchInlineSnapshot(`
|
||||
{
|
||||
"extends": "../../../.eslintrc.json",
|
||||
"ignorePatterns": [
|
||||
"!**/*",
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"*.ts",
|
||||
"*.tsx",
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/await-thenable": "off",
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
`);
|
||||
});
|
||||
|
||||
it('should not error if .eslintrc.json does not exist', async () => {
|
||||
await removeESLintProjectConfigIfNoTypeCheckingRules(tree);
|
||||
});
|
||||
});
|
||||
@ -1,38 +0,0 @@
|
||||
import { formatFiles, getProjects, readJson, updateJson } from '@nx/devkit';
|
||||
import type { Tree } from '@nx/devkit';
|
||||
import { join } from 'path';
|
||||
import {
|
||||
hasRulesRequiringTypeChecking,
|
||||
removeParserOptionsProjectIfNotRequired,
|
||||
} from '../../utils/rules-requiring-type-checking';
|
||||
|
||||
function updateProjectESLintConfigs(host: Tree) {
|
||||
const projects = getProjects(host);
|
||||
projects.forEach((p) => {
|
||||
const eslintConfigPath = join(p.root, '.eslintrc.json');
|
||||
if (!host.exists(eslintConfigPath)) {
|
||||
return;
|
||||
}
|
||||
return updateJson(
|
||||
host,
|
||||
eslintConfigPath,
|
||||
removeParserOptionsProjectIfNotRequired
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
export default async function removeESLintProjectConfigIfNoTypeCheckingRules(
|
||||
host: Tree
|
||||
) {
|
||||
if (!host.exists('.eslintrc.json')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If the root level config uses at least one rule requiring type-checking, do not migrate any project configs
|
||||
const rootESLintConfig = readJson(host, '.eslintrc.json');
|
||||
if (hasRulesRequiringTypeChecking(rootESLintConfig)) {
|
||||
return;
|
||||
}
|
||||
updateProjectESLintConfigs(host);
|
||||
await formatFiles(host);
|
||||
}
|
||||
@ -1,71 +0,0 @@
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
readProjectConfiguration,
|
||||
TargetConfiguration,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import addOutputs from './add-outputs';
|
||||
|
||||
describe('addOutputs', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
|
||||
const lintWithoutOutputs: TargetConfiguration = {
|
||||
executor: '@nrwl/linter:eslint',
|
||||
options: {},
|
||||
};
|
||||
const lintWithOutputs: TargetConfiguration = {
|
||||
executor: '@nrwl/linter:eslint',
|
||||
outputs: ['dist'],
|
||||
options: {},
|
||||
};
|
||||
const notLint: TargetConfiguration = {
|
||||
executor: '@nrwl/node:build',
|
||||
options: {},
|
||||
};
|
||||
|
||||
addProjectConfiguration(tree, 'proj', {
|
||||
root: 'proj',
|
||||
targets: {
|
||||
lintWithoutOutputs,
|
||||
lintWithOutputs,
|
||||
notLint,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('should add outputs to targets that do not have outputs', async () => {
|
||||
await addOutputs(tree);
|
||||
|
||||
expect(readProjectConfiguration(tree, 'proj')).toMatchInlineSnapshot(`
|
||||
{
|
||||
"$schema": "../node_modules/nx/schemas/project-schema.json",
|
||||
"name": "proj",
|
||||
"root": "proj",
|
||||
"targets": {
|
||||
"lintWithOutputs": {
|
||||
"executor": "@nrwl/linter:eslint",
|
||||
"options": {},
|
||||
"outputs": [
|
||||
"dist",
|
||||
],
|
||||
},
|
||||
"lintWithoutOutputs": {
|
||||
"executor": "@nrwl/linter:eslint",
|
||||
"options": {},
|
||||
"outputs": [
|
||||
"{options.outputFile}",
|
||||
],
|
||||
},
|
||||
"notLint": {
|
||||
"executor": "@nrwl/node:build",
|
||||
"options": {},
|
||||
},
|
||||
},
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
@ -1,26 +0,0 @@
|
||||
import {
|
||||
formatFiles,
|
||||
getProjects,
|
||||
Tree,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
|
||||
export default async function addOutputs(tree: Tree) {
|
||||
for (const [projectName, project] of getProjects(tree)) {
|
||||
if (!project.targets) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const target of Object.values(project.targets)) {
|
||||
if (target.executor !== '@nrwl/linter:eslint' || target.outputs) {
|
||||
continue;
|
||||
}
|
||||
|
||||
target.outputs = ['{options.outputFile}'];
|
||||
|
||||
updateProjectConfiguration(tree, projectName, project);
|
||||
}
|
||||
}
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
@ -1,115 +0,0 @@
|
||||
import { Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import eslint8Updates from './eslint-8-updates';
|
||||
|
||||
describe('eslint8Updates()', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
tree.write(
|
||||
`tools/eslint-rules/jest.config.js`,
|
||||
`
|
||||
module.exports = {
|
||||
displayName: 'eslint-rules',
|
||||
preset: '../../jest.preset.js',
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
},
|
||||
},
|
||||
transform: {
|
||||
'^.+\\.[tj]s$': 'ts-jest',
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'js', 'html'],
|
||||
coverageDirectory: '../../coverage/tools/eslint-rules',
|
||||
};
|
||||
`
|
||||
);
|
||||
|
||||
tree.write(
|
||||
`tools/eslint-rules/rules/existing-rule.ts`,
|
||||
`
|
||||
import { ESLintUtils } from '@typescript-eslint/experimental-utils';
|
||||
|
||||
// NOTE: The rule will be available in ESLint configs as "@nrwl/nx/workspace/existing-rule"
|
||||
export const RULE_NAME = 'existing-rule';
|
||||
|
||||
export const rule = ESLintUtils.RuleCreator(() => __filename)({
|
||||
name: RULE_NAME,
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: \`\`,
|
||||
category: 'Possible Errors',
|
||||
recommended: 'error',
|
||||
},
|
||||
schema: [],
|
||||
messages: {},
|
||||
},
|
||||
defaultOptions: [],
|
||||
create(context) {
|
||||
return {};
|
||||
},
|
||||
});
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
it('should add module mapping for ESLint to the jest config', async () => {
|
||||
await eslint8Updates(tree);
|
||||
|
||||
expect(tree.read('tools/eslint-rules/jest.config.js').toString('utf-8'))
|
||||
.toMatchInlineSnapshot(`
|
||||
"module.exports = {
|
||||
displayName: 'eslint-rules',
|
||||
preset: '../../jest.preset.js',
|
||||
globals: {
|
||||
'ts-jest': {
|
||||
tsconfig: '<rootDir>/tsconfig.spec.json',
|
||||
},
|
||||
},
|
||||
transform: {
|
||||
'^.+.[tj]s$': 'ts-jest',
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'js', 'html'],
|
||||
coverageDirectory: '../../coverage/tools/eslint-rules',
|
||||
moduleNameMapper: {
|
||||
'@eslint/eslintrc': '@eslint/eslintrc/dist/eslintrc-universal.cjs',
|
||||
},
|
||||
};
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should remove the category meta property from any existing workspace rules', async () => {
|
||||
await eslint8Updates(tree);
|
||||
|
||||
expect(
|
||||
tree.read(`tools/eslint-rules/rules/existing-rule.ts`).toString('utf-8')
|
||||
).toMatchInlineSnapshot(`
|
||||
"import { ESLintUtils } from '@typescript-eslint/experimental-utils';
|
||||
|
||||
// NOTE: The rule will be available in ESLint configs as "@nrwl/nx/workspace/existing-rule"
|
||||
export const RULE_NAME = 'existing-rule';
|
||||
|
||||
export const rule = ESLintUtils.RuleCreator(() => __filename)({
|
||||
name: RULE_NAME,
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: \`\`,
|
||||
recommended: 'error',
|
||||
},
|
||||
schema: [],
|
||||
messages: {},
|
||||
},
|
||||
defaultOptions: [],
|
||||
create(context) {
|
||||
return {};
|
||||
},
|
||||
});
|
||||
"
|
||||
`);
|
||||
});
|
||||
});
|
||||
@ -1,56 +0,0 @@
|
||||
import {
|
||||
ensurePackage,
|
||||
formatFiles,
|
||||
normalizePath,
|
||||
Tree,
|
||||
visitNotIgnoredFiles,
|
||||
} from '@nx/devkit';
|
||||
import { tsquery } from '@phenomnomnominal/tsquery';
|
||||
import { nxVersion } from '../../utils/versions';
|
||||
|
||||
export default async function eslint8Updates(tree: Tree) {
|
||||
try {
|
||||
const { addPropertyToJestConfig } = ensurePackage('@nx/jest', nxVersion);
|
||||
const existingJestConfigPath = normalizePath(
|
||||
'tools/eslint-rules/jest.config.js'
|
||||
);
|
||||
|
||||
// Add extra config to the jest.config.js file to allow ESLint 8 exports mapping to work with jest
|
||||
addPropertyToJestConfig(tree, existingJestConfigPath, 'moduleNameMapper', {
|
||||
'@eslint/eslintrc': '@eslint/eslintrc/dist/eslintrc-universal.cjs',
|
||||
});
|
||||
|
||||
visitNotIgnoredFiles(tree, 'tools/eslint-rules', (path) => {
|
||||
if (!path.endsWith('.ts')) {
|
||||
return;
|
||||
}
|
||||
const fileContents = tree.read(path).toString('utf-8');
|
||||
const fileAst = tsquery.ast(fileContents);
|
||||
const isESLintRuleFile =
|
||||
tsquery(
|
||||
fileAst,
|
||||
'PropertyAccessExpression[expression.escapedText=ESLintUtils][name.escapedText=RuleCreator]'
|
||||
).length > 0;
|
||||
if (!isESLintRuleFile) {
|
||||
return;
|
||||
}
|
||||
const categoryPropertyAssignmentNode = tsquery(
|
||||
fileAst,
|
||||
'PropertyAssignment[name.escapedText=meta] PropertyAssignment[name.escapedText=docs] PropertyAssignment[name.escapedText=category]'
|
||||
)[0];
|
||||
if (!categoryPropertyAssignmentNode) {
|
||||
return;
|
||||
}
|
||||
let end = categoryPropertyAssignmentNode.getEnd();
|
||||
if (fileContents.substring(end, end + 1) === ',') {
|
||||
end++;
|
||||
}
|
||||
const updatedContents =
|
||||
fileContents.slice(0, categoryPropertyAssignmentNode.getFullStart()) +
|
||||
fileContents.slice(end);
|
||||
tree.write(path, updatedContents);
|
||||
});
|
||||
|
||||
await formatFiles(tree);
|
||||
} catch {}
|
||||
}
|
||||
@ -1,13 +0,0 @@
|
||||
import { formatFiles, Tree } from '@nx/devkit';
|
||||
import { addSwcRegisterDependencies } from '@nx/js/src/utils/swc/add-swc-dependencies';
|
||||
import { WORKSPACE_PLUGIN_DIR } from '../../generators/workspace-rules-project/workspace-rules-project';
|
||||
|
||||
export default async function addSwcNodeIfNeeded(tree: Tree) {
|
||||
try {
|
||||
if (tree.exists(WORKSPACE_PLUGIN_DIR)) {
|
||||
addSwcRegisterDependencies(tree);
|
||||
await formatFiles(tree);
|
||||
return;
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
@ -1,36 +0,0 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`addTypescriptEslintUtilsIfNeeded() should add @typescript-eslint/utils if plugins folder exists 1`] = `
|
||||
"{
|
||||
"name": "test-name",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/utils": "^5.60.1"
|
||||
}
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`addTypescriptEslintUtilsIfNeeded() should not add @typescript-eslint/utils if plugins folder or experimental-utils dont exist 1`] = `"{"name":"test-name","dependencies":{},"devDependencies":{}}"`;
|
||||
|
||||
exports[`addTypescriptEslintUtilsIfNeeded() should remove @typescript-eslint/experimental-utils from package.json devDependencies and add @typescript-eslint/utils 1`] = `
|
||||
"{
|
||||
"name": "test-name",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/utils": "^5.60.1"
|
||||
}
|
||||
}
|
||||
"
|
||||
`;
|
||||
|
||||
exports[`addTypescriptEslintUtilsIfNeeded() should remove @typescript-eslint/experimental-utils from package.json devDependencies and add @typescript-eslint/utils 2`] = `
|
||||
"{
|
||||
"name": "test-name",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/utils": "^5.60.1"
|
||||
}
|
||||
}
|
||||
"
|
||||
`;
|
||||
@ -1,51 +0,0 @@
|
||||
import { updateJson } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { WORKSPACE_PLUGIN_DIR } from '../../generators/workspace-rules-project/workspace-rules-project';
|
||||
import addTypescriptEslintUtilsIfNeeded from './experimental-to-utils-deps';
|
||||
|
||||
describe('addTypescriptEslintUtilsIfNeeded()', () => {
|
||||
it('should remove @typescript-eslint/experimental-utils from package.json devDependencies and add @typescript-eslint/utils', async () => {
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
json.devDependencies['@typescript-eslint/experimental-utils'] = '12.3.4';
|
||||
return json;
|
||||
});
|
||||
|
||||
await addTypescriptEslintUtilsIfNeeded(tree);
|
||||
|
||||
expect(tree.read(`package.json`).toString('utf-8')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should remove @typescript-eslint/experimental-utils from package.json devDependencies and add @typescript-eslint/utils', async () => {
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
json.dependencies['@typescript-eslint/experimental-utils'] = '12.3.4';
|
||||
return json;
|
||||
});
|
||||
|
||||
await addTypescriptEslintUtilsIfNeeded(tree);
|
||||
|
||||
expect(tree.read(`package.json`).toString('utf-8')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should add @typescript-eslint/utils if plugins folder exists', async () => {
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
|
||||
tree.write(
|
||||
`${WORKSPACE_PLUGIN_DIR}/rules/my-rule.ts`,
|
||||
'console.log("not important");'
|
||||
);
|
||||
|
||||
await addTypescriptEslintUtilsIfNeeded(tree);
|
||||
|
||||
expect(tree.read(`package.json`).toString('utf-8')).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should not add @typescript-eslint/utils if plugins folder or experimental-utils dont exist', async () => {
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
|
||||
await addTypescriptEslintUtilsIfNeeded(tree);
|
||||
|
||||
expect(tree.read(`package.json`).toString('utf-8')).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@ -1,45 +0,0 @@
|
||||
import {
|
||||
addDependenciesToPackageJson,
|
||||
formatFiles,
|
||||
readJson,
|
||||
removeDependenciesFromPackageJson,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
import { WORKSPACE_PLUGIN_DIR } from '../../generators/workspace-rules-project/workspace-rules-project';
|
||||
import { typescriptESLintVersion } from '../../utils/versions';
|
||||
|
||||
export default async function addTypescriptEslintUtilsIfNeeded(tree: Tree) {
|
||||
try {
|
||||
const packageJson = readJson(tree, 'package.json');
|
||||
let removed = false;
|
||||
|
||||
if (packageJson.devDependencies['@typescript-eslint/experimental-utils']) {
|
||||
await removeDependenciesFromPackageJson(
|
||||
tree,
|
||||
[],
|
||||
['@typescript-eslint/experimental-utils']
|
||||
);
|
||||
removed = true;
|
||||
}
|
||||
|
||||
if (packageJson.dependencies['@typescript-eslint/experimental-utils']) {
|
||||
await removeDependenciesFromPackageJson(
|
||||
tree,
|
||||
['@typescript-eslint/experimental-utils'],
|
||||
[]
|
||||
);
|
||||
removed = true;
|
||||
}
|
||||
|
||||
if (removed || tree.exists(WORKSPACE_PLUGIN_DIR)) {
|
||||
addDependenciesToPackageJson(
|
||||
tree,
|
||||
{},
|
||||
{ '@typescript-eslint/utils': typescriptESLintVersion }
|
||||
);
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
return;
|
||||
} catch {}
|
||||
}
|
||||
@ -1,132 +0,0 @@
|
||||
import { Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import experimentalToUtilsUpdate from './experimental-to-utils-rules';
|
||||
|
||||
describe('experimentalToUtilsUpdate()', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
tree.write(
|
||||
`tools/eslint-rules/rules/existing-rule.ts`,
|
||||
`
|
||||
import { ESLintUtils } from '@typescript-eslint/experimental-utils';
|
||||
import { rule, RULE_NAME } from './existing-rule';
|
||||
|
||||
// NOTE: The rule will be available in ESLint configs as "@nrwl/nx/workspace/existing-rule"
|
||||
export const RULE_NAME = 'existing-rule';
|
||||
|
||||
export const rule = ESLintUtils.RuleCreator(() => __filename)({
|
||||
name: RULE_NAME,
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: \`\`,
|
||||
recommended: 'error',
|
||||
},
|
||||
schema: [],
|
||||
messages: {},
|
||||
},
|
||||
defaultOptions: [],
|
||||
create(context) {
|
||||
return {};
|
||||
},
|
||||
});
|
||||
`
|
||||
);
|
||||
|
||||
tree.write(
|
||||
`tools/eslint-rules/rules/existing-rule.spec.ts`,
|
||||
`
|
||||
import { TSESLint } from '@typescript-eslint/experimental-utils';
|
||||
import { rule, RULE_NAME } from './existing-rule';
|
||||
|
||||
const ruleTester = new TSESLint.RuleTester({
|
||||
parser: require.resolve('@typescript-eslint/parser'),
|
||||
});
|
||||
|
||||
ruleTester.run(RULE_NAME, rule, {
|
||||
valid: [\`const example = true;\`],
|
||||
invalid: [],
|
||||
});
|
||||
`
|
||||
);
|
||||
|
||||
tree.write(
|
||||
`tools/eslint-rules/rules/multi-import.ts`,
|
||||
`
|
||||
import { ESLintUtils } from '@typescript-eslint/experimental-utils';
|
||||
import { TSESLint } from '@typescript-eslint/experimental-utils';
|
||||
import { rule, RULE_NAME } from './existing-rule';
|
||||
|
||||
// NOTE: remaining code is irrelevant for this test
|
||||
`
|
||||
);
|
||||
});
|
||||
|
||||
it('should switch import from @typescript-eslint/experimental-utils to @typescript-eslint/utils in rule and spec file', async () => {
|
||||
await experimentalToUtilsUpdate(tree);
|
||||
|
||||
expect(
|
||||
tree.read(`tools/eslint-rules/rules/existing-rule.ts`).toString('utf-8')
|
||||
).toMatchInlineSnapshot(`
|
||||
"import { ESLintUtils } from '@typescript-eslint/utils';
|
||||
import { rule, RULE_NAME } from './existing-rule';
|
||||
|
||||
// NOTE: The rule will be available in ESLint configs as "@nrwl/nx/workspace/existing-rule"
|
||||
export const RULE_NAME = 'existing-rule';
|
||||
|
||||
export const rule = ESLintUtils.RuleCreator(() => __filename)({
|
||||
name: RULE_NAME,
|
||||
meta: {
|
||||
type: 'problem',
|
||||
docs: {
|
||||
description: \`\`,
|
||||
recommended: 'error',
|
||||
},
|
||||
schema: [],
|
||||
messages: {},
|
||||
},
|
||||
defaultOptions: [],
|
||||
create(context) {
|
||||
return {};
|
||||
},
|
||||
});
|
||||
"
|
||||
`);
|
||||
|
||||
expect(
|
||||
tree
|
||||
.read(`tools/eslint-rules/rules/existing-rule.spec.ts`)
|
||||
.toString('utf-8')
|
||||
).toMatchInlineSnapshot(`
|
||||
"import { TSESLint } from '@typescript-eslint/utils';
|
||||
import { rule, RULE_NAME } from './existing-rule';
|
||||
|
||||
const ruleTester = new TSESLint.RuleTester({
|
||||
parser: require.resolve('@typescript-eslint/parser'),
|
||||
});
|
||||
|
||||
ruleTester.run(RULE_NAME, rule, {
|
||||
valid: [\`const example = true;\`],
|
||||
invalid: [],
|
||||
});
|
||||
"
|
||||
`);
|
||||
});
|
||||
|
||||
it('should switch import from @typescript-eslint/experimental-utils to @typescript-eslint/utils on multiple imports', async () => {
|
||||
await experimentalToUtilsUpdate(tree);
|
||||
|
||||
expect(
|
||||
tree.read(`tools/eslint-rules/rules/multi-import.ts`).toString('utf-8')
|
||||
).toMatchInlineSnapshot(`
|
||||
"import { ESLintUtils } from '@typescript-eslint/utils';
|
||||
import { TSESLint } from '@typescript-eslint/utils';
|
||||
import { rule, RULE_NAME } from './existing-rule';
|
||||
|
||||
// NOTE: remaining code is irrelevant for this test
|
||||
"
|
||||
`);
|
||||
});
|
||||
});
|
||||
@ -1,39 +0,0 @@
|
||||
import { formatFiles, Tree, visitNotIgnoredFiles } from '@nx/devkit';
|
||||
import { tsquery } from '@phenomnomnominal/tsquery';
|
||||
|
||||
function updateFile(fileAst: any, fileContents: string): string | undefined {
|
||||
const importStatement: any[] = tsquery(
|
||||
fileAst,
|
||||
'ImportDeclaration StringLiteral[value=@typescript-eslint/experimental-utils]'
|
||||
);
|
||||
if (importStatement.length === 0) {
|
||||
return;
|
||||
}
|
||||
const contentSlices = fileContents.split(
|
||||
'@typescript-eslint/experimental-utils'
|
||||
);
|
||||
let updatedFileContents = '';
|
||||
for (let i = 0; i < contentSlices.length / 2; i++) {
|
||||
updatedFileContents += `${contentSlices[i]}@typescript-eslint/utils`;
|
||||
}
|
||||
updatedFileContents += contentSlices[contentSlices.length - 1];
|
||||
return updatedFileContents;
|
||||
}
|
||||
|
||||
export default async function experimentalToUtilsUpdate(tree: Tree) {
|
||||
try {
|
||||
visitNotIgnoredFiles(tree, 'tools/eslint-rules', (path) => {
|
||||
if (path.endsWith('.ts')) {
|
||||
const fileContents = tree.read(path).toString('utf-8');
|
||||
const fileAst = tsquery.ast(fileContents);
|
||||
|
||||
const updatedContents = updateFile(fileAst, fileContents);
|
||||
if (updatedContents) {
|
||||
tree.write(path, updatedContents);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await formatFiles(tree);
|
||||
} catch {}
|
||||
}
|
||||
@ -1,11 +1,5 @@
|
||||
{
|
||||
"generators": {
|
||||
"update-to-nest-8": {
|
||||
"cli": "nx",
|
||||
"version": "13.2.0-beta.0",
|
||||
"description": "Update Nest.js libraries",
|
||||
"factory": "./src/migrations/update-13-2-0/update-to-nest-8"
|
||||
},
|
||||
"update-16-0-0-add-nx-packages": {
|
||||
"cli": "nx",
|
||||
"version": "16.0.0-beta.1",
|
||||
@ -20,35 +14,6 @@
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
"14.5.0": {
|
||||
"version": "14.5.0-beta.1",
|
||||
"packages": {
|
||||
"@nestjs/common": {
|
||||
"version": "^9.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@nestjs/core": {
|
||||
"version": "^9.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@nestjs/platform-express": {
|
||||
"version": "^9.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@nestjs/schematics": {
|
||||
"version": "^9.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@nestjs/swagger": {
|
||||
"version": "^6.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"@nestjs/testing": {
|
||||
"version": "^9.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"16.1.0": {
|
||||
"version": "16.1.0-beta.0",
|
||||
"packages": {
|
||||
|
||||
@ -31,13 +31,11 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/schematics": "^9.1.0",
|
||||
"enquirer": "~2.3.6",
|
||||
"@nx/devkit": "file:../devkit",
|
||||
"@nx/js": "file:../js",
|
||||
"@nx/linter": "file:../linter",
|
||||
"@nx/node": "file:../node",
|
||||
"@phenomnomnominal/tsquery": "~5.0.1",
|
||||
"semver": "7.5.3",
|
||||
"tslib": "^2.3.0"
|
||||
},
|
||||
"publishConfig": {
|
||||
|
||||
@ -1,89 +0,0 @@
|
||||
import { formatFiles, logger, readJson, Tree, updateJson } from '@nx/devkit';
|
||||
import { checkAndCleanWithSemver } from '@nx/devkit/src/utils/semver';
|
||||
import { satisfies } from 'semver';
|
||||
import { sortObjectByKeys } from 'nx/src/utils/object-sort';
|
||||
|
||||
const nestJsSchematicsVersion = '^9.0.0';
|
||||
const nestJsVersion8 = '^8.0.0';
|
||||
const rxjsVersion7 = '^7.0.0';
|
||||
|
||||
export default async function update(tree: Tree) {
|
||||
const shouldUpdate = await isUpdatable(tree);
|
||||
|
||||
if (!shouldUpdate) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateVersion(tree);
|
||||
|
||||
await formatFiles(tree);
|
||||
|
||||
return (): void => {
|
||||
logger.info(
|
||||
'Please make sure to run npm install or yarn install to get the latest packages added by this migration'
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
async function isUpdatable(tree: Tree) {
|
||||
const json = readJson(tree, 'package.json');
|
||||
|
||||
if (json.dependencies['@angular/core']) {
|
||||
const rxjs = checkAndCleanWithSemver('rxjs', json.dependencies['rxjs']);
|
||||
if (satisfies(rxjs, rxjsVersion7)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const { Confirm } = require('enquirer');
|
||||
const prompt = new Confirm({
|
||||
name: 'question',
|
||||
message: 'Do you want to update to RxJS 7 + Nest 8?',
|
||||
initial: true,
|
||||
});
|
||||
|
||||
return await prompt.run();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function updateVersion(tree: Tree) {
|
||||
updateJson(tree, 'package.json', (json) => {
|
||||
json.dependencies = json.dependencies || {};
|
||||
json.devDependencies = json.devDependencies || {};
|
||||
|
||||
const rxjs = checkAndCleanWithSemver('rxjs', json.dependencies['rxjs']);
|
||||
|
||||
json.dependencies = {
|
||||
...json.dependencies,
|
||||
'@nestjs/common': nestJsVersion8,
|
||||
'@nestjs/core': nestJsVersion8,
|
||||
rxjs: satisfies(rxjs, rxjsVersion7)
|
||||
? json.dependencies['rxjs']
|
||||
: rxjsVersion7,
|
||||
};
|
||||
|
||||
if (json.dependencies['@nestjs/platform-express']) {
|
||||
json.dependencies['@nestjs/platform-express'] = nestJsVersion8;
|
||||
}
|
||||
|
||||
if (json.dependencies['@nestjs/platform-fastify']) {
|
||||
json.dependencies['@nestjs/platform-fastify'] = nestJsVersion8;
|
||||
}
|
||||
|
||||
json.devDependencies = {
|
||||
...json.devDependencies,
|
||||
'@nestjs/schematics': nestJsSchematicsVersion,
|
||||
'@nestjs/testing': nestJsVersion8,
|
||||
};
|
||||
|
||||
if (json.devDependencies['jasmine-marbles']) {
|
||||
json.devDependencies['jasmine-marbles'] = '~0.9.1';
|
||||
}
|
||||
|
||||
json.dependencies = sortObjectByKeys(json.dependencies);
|
||||
json.devDependencies = sortObjectByKeys(json.devDependencies);
|
||||
|
||||
return json;
|
||||
});
|
||||
}
|
||||
@ -1,59 +1,5 @@
|
||||
{
|
||||
"generators": {
|
||||
"fix-nextjs-lib-babel-config-12.8.0": {
|
||||
"cli": "nx",
|
||||
"version": "12.8.0-beta.11",
|
||||
"description": "Adjust the Next.js lib babel configuration for styled-jsx",
|
||||
"factory": "./src/migrations/update-12-8-0/remove-styled-jsx-babel-plugin"
|
||||
},
|
||||
"fix-page-dir-for-eslint": {
|
||||
"cli": "nx",
|
||||
"version": "12.10.0-beta.1",
|
||||
"description": "Updates .eslintrc.json file to use the correct pages directory for '@next/next/no-html-link-for-pages'.",
|
||||
"factory": "./src/migrations/update-12-10-0/fix-page-dir-for-eslint"
|
||||
},
|
||||
"update-emotion-setup-13.0.0": {
|
||||
"cli": "nx",
|
||||
"version": "13.0.0-beta.0",
|
||||
"description": "Update tsconfig.json to use `jsxImportSource` to support css prop",
|
||||
"factory": "./src/migrations/update-13-0-0/update-emotion-setup"
|
||||
},
|
||||
"update-to-webpack-5": {
|
||||
"cli": "nx",
|
||||
"version": "13.0.0-beta.1",
|
||||
"description": "Set `webpack5: true` for all next.js projects.",
|
||||
"factory": "./src/migrations/update-13-0-0/update-to-webpack-5"
|
||||
},
|
||||
"fix-less": {
|
||||
"cli": "nx",
|
||||
"version": "13.0.3-beta.1",
|
||||
"description": "Fix setup for less stylesheets",
|
||||
"factory": "./src/migrations/update-13-0-3/fix-less"
|
||||
},
|
||||
"add-default-development-configurations-14.0.0": {
|
||||
"cli": "nx",
|
||||
"version": "14.0.0-beta.0",
|
||||
"description": "Add a default development configuration for build and serve targets.",
|
||||
"factory": "./src/migrations/update-14-0-0/add-default-development-configurations"
|
||||
},
|
||||
"add-dev-output-path": {
|
||||
"cli": "nx",
|
||||
"version": "14.4.3-beta.0",
|
||||
"description": "Add a development outputPath to avoid conflict with the production build.",
|
||||
"factory": "./src/migrations/update-14-4-3/add-dev-output-path"
|
||||
},
|
||||
"add-gitignore-entry": {
|
||||
"cli": "nx",
|
||||
"version": "14.5.3-beta.0",
|
||||
"description": "Add .next folder to gitignore.",
|
||||
"factory": "./src/migrations/update-14-5-3/add-gitignore-entry"
|
||||
},
|
||||
"update-dev-output-path": {
|
||||
"cli": "nx",
|
||||
"version": "14.5.3-beta.0",
|
||||
"description": "Update development outputPath to the project root.",
|
||||
"factory": "./src/migrations/update-14-5-3/update-dev-output-path"
|
||||
},
|
||||
"add-style-packages": {
|
||||
"cli": "nx",
|
||||
"version": "15.8.8-beta.0",
|
||||
@ -80,212 +26,6 @@
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
"12.1.2": {
|
||||
"version": "12.1.2-beta.0",
|
||||
"packages": {
|
||||
"next": {
|
||||
"version": "10.2.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"12.6.0": {
|
||||
"version": "12.6.0-beta.0",
|
||||
"packages": {
|
||||
"next": {
|
||||
"version": "11.0.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"eslint-config-next": {
|
||||
"version": "11.0.1",
|
||||
"addToPackageJson": "devDependencies"
|
||||
}
|
||||
}
|
||||
},
|
||||
"update-12.7.2": {
|
||||
"version": "12.7.2-beta.0",
|
||||
"packages": {
|
||||
"next": {
|
||||
"version": "11.1.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"eslint-config-next": {
|
||||
"version": "11.1.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"update-13.0.3": {
|
||||
"version": "13.0.3-beta.0",
|
||||
"packages": {
|
||||
"next": {
|
||||
"version": "12.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"eslint-config-next": {
|
||||
"version": "12.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"13.3.0": {
|
||||
"version": "13.3.0-beta.0",
|
||||
"packages": {
|
||||
"next": {
|
||||
"version": "12.0.7",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"eslint-config-next": {
|
||||
"version": "12.0.7",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"13.9.0": {
|
||||
"version": "13.9.0-beta.0",
|
||||
"packages": {
|
||||
"next": {
|
||||
"version": "12.1.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"eslint-config-next": {
|
||||
"version": "12.1.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"13.10.0": {
|
||||
"version": "13.10.0-beta.1",
|
||||
"packages": {
|
||||
"next": {
|
||||
"version": "12.1.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"eslint-config-next": {
|
||||
"version": "12.1.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"sass": {
|
||||
"version": "1.49.9",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.0.0": {
|
||||
"version": "14.0.0-beta.1",
|
||||
"packages": {
|
||||
"next": {
|
||||
"version": "12.1.5",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"eslint-config-next": {
|
||||
"version": "12.1.5",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.2.3": {
|
||||
"version": "14.2.3-beta.0",
|
||||
"packages": {
|
||||
"next": {
|
||||
"version": "12.1.6",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"eslint-config-next": {
|
||||
"version": "12.1.6",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"sass": {
|
||||
"version": "1.52.3",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"less-loader": {
|
||||
"version": "11.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"stylus-loader": {
|
||||
"version": "7.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.4.3": {
|
||||
"version": "14.4.3",
|
||||
"packages": {
|
||||
"next": {
|
||||
"version": "12.2.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"eslint-config-next": {
|
||||
"version": "12.2.2",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"sass": {
|
||||
"version": "1.53.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.5.0": {
|
||||
"version": "14.5.0-beta.0",
|
||||
"packages": {
|
||||
"next": {
|
||||
"version": "12.2.3",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"eslint-config-next": {
|
||||
"version": "12.2.3",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"sass": {
|
||||
"version": "1.54.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.5.3": {
|
||||
"version": "14.5.3-beta.0",
|
||||
"packages": {
|
||||
"@emotion/server": {
|
||||
"version": "11.10.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.6.0": {
|
||||
"version": "14.6.0-beta.1",
|
||||
"packages": {
|
||||
"next": {
|
||||
"version": "12.2.5",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"eslint-config-next": {
|
||||
"version": "12.2.5",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"sass": {
|
||||
"version": "1.54.5",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.7.10": {
|
||||
"version": "14.7.10-beta.1",
|
||||
"packages": {
|
||||
"next": {
|
||||
"version": "12.3.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"eslint-config-next": {
|
||||
"version": "12.3.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
},
|
||||
"sass": {
|
||||
"version": "1.55.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"15.0.4": {
|
||||
"version": "15.0.4-beta.0",
|
||||
"packages": {
|
||||
|
||||
@ -1,114 +0,0 @@
|
||||
import { readJson, Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
|
||||
import { applicationGenerator } from '../../generators/application/application';
|
||||
import { fixPageDirForEslint } from './fix-page-dir-for-eslint';
|
||||
|
||||
describe('Migration: Fix pages directory for ESLint', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
|
||||
await applicationGenerator(tree, {
|
||||
style: 'none',
|
||||
name: 'demo',
|
||||
skipFormat: false,
|
||||
});
|
||||
});
|
||||
|
||||
it('should fix custom pages path', async () => {
|
||||
// Config that isn't configured properly
|
||||
tree.write(
|
||||
'apps/demo/.eslintrc.json',
|
||||
JSON.stringify({
|
||||
extends: ['next'],
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
|
||||
rules: {
|
||||
'existing-rule': 'error',
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
await fixPageDirForEslint(tree);
|
||||
|
||||
const result = readJson(tree, 'apps/demo/.eslintrc.json');
|
||||
expect(result.overrides[0].rules).toEqual({
|
||||
'existing-rule': 'error',
|
||||
'@next/next/no-html-link-for-pages': ['error', 'apps/demo/pages'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should leave existing no-html-link-for-pages rule if it exists', async () => {
|
||||
// Config that isn't configured properly
|
||||
tree.write(
|
||||
'apps/demo/.eslintrc.json',
|
||||
JSON.stringify({
|
||||
extends: ['next'],
|
||||
overrides: [
|
||||
{
|
||||
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
|
||||
rules: {
|
||||
'@next/next/no-html-link-for-pages': [
|
||||
'error',
|
||||
'apps/demo/some/other/path',
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
await fixPageDirForEslint(tree);
|
||||
|
||||
const result = readJson(tree, 'apps/demo/.eslintrc.json');
|
||||
expect(result.overrides[0].rules).toEqual({
|
||||
'@next/next/no-html-link-for-pages': [
|
||||
'error',
|
||||
'apps/demo/some/other/path',
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle custom overrides configuration', async () => {
|
||||
// Config that isn't configured properly
|
||||
tree.write(
|
||||
'apps/demo/.eslintrc.json',
|
||||
JSON.stringify({
|
||||
extends: ['next', 'plugin:vue/base'],
|
||||
overrides: [
|
||||
{ files: ['*.vue'], rule: { 'vue/comment-directive': 'error' } },
|
||||
{
|
||||
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
|
||||
rules: {
|
||||
'@next/next/no-html-link-for-pages': [
|
||||
'error',
|
||||
'apps/demo/some/other/path',
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
await fixPageDirForEslint(tree);
|
||||
|
||||
const result = readJson(tree, 'apps/demo/.eslintrc.json');
|
||||
expect(result.overrides).toEqual([
|
||||
{ files: ['*.vue'], rule: { 'vue/comment-directive': 'error' } },
|
||||
{
|
||||
files: ['*.ts', '*.tsx', '*.js', '*.jsx'],
|
||||
rules: {
|
||||
'@next/next/no-html-link-for-pages': [
|
||||
'error',
|
||||
'apps/demo/some/other/path',
|
||||
],
|
||||
},
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
@ -1,45 +0,0 @@
|
||||
import {
|
||||
formatFiles,
|
||||
getProjects,
|
||||
joinPathFragments,
|
||||
readJson,
|
||||
Tree,
|
||||
writeJson,
|
||||
} from '@nx/devkit';
|
||||
|
||||
export async function fixPageDirForEslint(host: Tree) {
|
||||
const projects = getProjects(host);
|
||||
|
||||
projects.forEach((project) => {
|
||||
const eslintRcJson = joinPathFragments(project.root, '.eslintrc.json');
|
||||
if (host.exists(eslintRcJson)) {
|
||||
const config = readJson(host, eslintRcJson);
|
||||
// Ignore non-nextjs projects
|
||||
if (!config?.extends?.includes('next')) return;
|
||||
|
||||
// Find the override that handles both TS and JS files.
|
||||
const commonOverride = config.overrides?.find((o) =>
|
||||
['*.ts', '*.tsx', '*.js', '*.jsx'].every((ext) => o.files.includes(ext))
|
||||
);
|
||||
|
||||
if (
|
||||
commonOverride &&
|
||||
!commonOverride.rules['@next/next/no-html-link-for-pages']
|
||||
) {
|
||||
commonOverride.rules = {
|
||||
...commonOverride.rules,
|
||||
'@next/next/no-html-link-for-pages': [
|
||||
'error',
|
||||
`${project.root}/pages`,
|
||||
],
|
||||
};
|
||||
|
||||
writeJson(host, eslintRcJson, config);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await formatFiles(host);
|
||||
}
|
||||
|
||||
export default fixPageDirForEslint;
|
||||
@ -1,56 +0,0 @@
|
||||
import { readJson, Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { Linter } from '@nx/linter';
|
||||
import { libraryGenerator } from '../../generators/library/library';
|
||||
import { removeStyledJsxBabelConfig } from './remove-styled-jsx-babel-plugin';
|
||||
|
||||
describe('Remove styled-jsx babel plugin for Next libs', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
|
||||
await libraryGenerator(tree, {
|
||||
name: 'test-lib',
|
||||
style: 'styled-jsx',
|
||||
skipFormat: false,
|
||||
skipTsConfig: false,
|
||||
linter: Linter.EsLint,
|
||||
unitTestRunner: 'jest',
|
||||
});
|
||||
});
|
||||
|
||||
it('should remove styled-jsx babel plugin', async () => {
|
||||
tree.write(
|
||||
'libs/test-lib/.babelrc',
|
||||
JSON.stringify({
|
||||
presets: ['next/babel'],
|
||||
plugins: ['styled-jsx/babel'],
|
||||
})
|
||||
);
|
||||
|
||||
await removeStyledJsxBabelConfig(tree);
|
||||
const result = readJson(tree, 'libs/test-lib/.babelrc');
|
||||
expect(result.plugins).toEqual([]);
|
||||
});
|
||||
it('should remove styled-jsx babel plugin but leave potentially other plugins in there', async () => {
|
||||
tree.write(
|
||||
'libs/test-lib/.babelrc',
|
||||
JSON.stringify({
|
||||
presets: ['next/babel'],
|
||||
plugins: [
|
||||
'some-other/plugin',
|
||||
'styled-jsx/babel',
|
||||
'some-storybook/plugin',
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
await removeStyledJsxBabelConfig(tree);
|
||||
const result = readJson(tree, 'libs/test-lib/.babelrc');
|
||||
expect(result.plugins).toEqual([
|
||||
'some-other/plugin',
|
||||
'some-storybook/plugin',
|
||||
]);
|
||||
});
|
||||
});
|
||||
@ -1,33 +0,0 @@
|
||||
import {
|
||||
formatFiles,
|
||||
getProjects,
|
||||
Tree,
|
||||
readJson,
|
||||
joinPathFragments,
|
||||
writeJson,
|
||||
} from '@nx/devkit';
|
||||
|
||||
export async function removeStyledJsxBabelConfig(host: Tree) {
|
||||
const projects = getProjects(host);
|
||||
|
||||
projects.forEach((project) => {
|
||||
const babelRcPath = joinPathFragments(project.root, '.babelrc');
|
||||
if (host.exists(babelRcPath)) {
|
||||
const babelRcContent = readJson(host, babelRcPath);
|
||||
|
||||
// check whether next.js project
|
||||
if (babelRcContent.presets.includes('next/babel')) {
|
||||
babelRcContent.plugins = babelRcContent.plugins.filter(
|
||||
(x) => x !== 'styled-jsx/babel'
|
||||
);
|
||||
|
||||
// update .babelrc
|
||||
writeJson(host, babelRcPath, babelRcContent);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await formatFiles(host);
|
||||
}
|
||||
|
||||
export default removeStyledJsxBabelConfig;
|
||||
@ -1,85 +0,0 @@
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { addProjectConfiguration, readJson, Tree } from '@nx/devkit';
|
||||
import { updateEmotionSetup } from './update-emotion-setup';
|
||||
|
||||
describe('Update tsconfig config for Emotion', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
});
|
||||
|
||||
it(`should add jsxImportSource if it uses @emotion/react`, async () => {
|
||||
addProjectConfiguration(tree, 'no-emotion-app', {
|
||||
root: 'apps/no-emotion-app',
|
||||
projectType: 'application',
|
||||
});
|
||||
addProjectConfiguration(tree, 'plain-next-app', {
|
||||
root: 'apps/plain-next-app',
|
||||
projectType: 'application',
|
||||
});
|
||||
addProjectConfiguration(tree, 'emotion-app', {
|
||||
root: 'apps/emotion-app',
|
||||
projectType: 'application',
|
||||
});
|
||||
tree.write(
|
||||
'nx.json',
|
||||
JSON.stringify({
|
||||
projects: {
|
||||
'no-emotion-app': {},
|
||||
'plain-next-app': {},
|
||||
'emotion-app': {},
|
||||
},
|
||||
})
|
||||
);
|
||||
tree.write('apps/no-emotion-app/.babelrc', JSON.stringify({}));
|
||||
tree.write(
|
||||
'apps/no-emotion-app/tsconfig.json',
|
||||
JSON.stringify({ compilerOptions: {} })
|
||||
);
|
||||
tree.write(
|
||||
'apps/plain-next-app/.babelrc',
|
||||
JSON.stringify({
|
||||
presets: ['@nrwl/next/babel'],
|
||||
plugins: [],
|
||||
})
|
||||
);
|
||||
tree.write(
|
||||
'apps/plain-next-app/tsconfig.json',
|
||||
JSON.stringify({ compilerOptions: { jsx: 'preserve' } })
|
||||
);
|
||||
tree.write(
|
||||
'apps/emotion-app/.babelrc',
|
||||
JSON.stringify({
|
||||
presets: [
|
||||
[
|
||||
'@nrwl/next/babel',
|
||||
{
|
||||
'preset-react': {
|
||||
runtime: 'automatic',
|
||||
importSource: '@emotion/react',
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
plugins: ['@emotion/babel-plugin'],
|
||||
})
|
||||
);
|
||||
tree.write(
|
||||
'apps/emotion-app/tsconfig.json',
|
||||
JSON.stringify({ compilerOptions: { jsx: 'preserve' } })
|
||||
);
|
||||
|
||||
await updateEmotionSetup(tree);
|
||||
|
||||
expect(readJson(tree, 'apps/no-emotion-app/tsconfig.json')).toEqual({
|
||||
compilerOptions: {},
|
||||
});
|
||||
expect(readJson(tree, 'apps/plain-next-app/tsconfig.json')).toEqual({
|
||||
compilerOptions: { jsx: 'preserve' },
|
||||
});
|
||||
expect(readJson(tree, 'apps/emotion-app/tsconfig.json')).toEqual({
|
||||
compilerOptions: { jsx: 'preserve', jsxImportSource: '@emotion/react' },
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,42 +0,0 @@
|
||||
import {
|
||||
formatFiles,
|
||||
getProjects,
|
||||
Tree,
|
||||
readJson,
|
||||
updateJson,
|
||||
} from '@nx/devkit';
|
||||
|
||||
export async function updateEmotionSetup(host: Tree) {
|
||||
const projects = getProjects(host);
|
||||
|
||||
projects.forEach((p) => {
|
||||
let hasEmotion = false;
|
||||
const babelrcPath = `${p.root}/.babelrc`;
|
||||
const tsConfigPath = `${p.root}/tsconfig.json`;
|
||||
|
||||
if (host.exists(babelrcPath)) {
|
||||
const babelrc = readJson(host, babelrcPath);
|
||||
if (babelrc.presets) {
|
||||
for (const [idx, preset] of babelrc.presets.entries()) {
|
||||
if (Array.isArray(preset)) {
|
||||
if (!preset[0].includes('@nrwl/next/babel')) continue;
|
||||
const emotionOptions = preset[1]['preset-react'];
|
||||
hasEmotion = emotionOptions?.importSource === '@emotion/react';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hasEmotion && host.exists(tsConfigPath)) {
|
||||
updateJson(host, tsConfigPath, (json) => {
|
||||
json.compilerOptions.jsxImportSource = '@emotion/react';
|
||||
return json;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
await formatFiles(host);
|
||||
}
|
||||
|
||||
export default updateEmotionSetup;
|
||||
@ -1,97 +0,0 @@
|
||||
import { Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
|
||||
import { applicationGenerator } from '../../generators/application/application';
|
||||
import { update } from './update-to-webpack-5';
|
||||
|
||||
describe('Migration: enable webpack 5', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
});
|
||||
|
||||
it('should set webpack5 to true', async () => {
|
||||
await applicationGenerator(tree, {
|
||||
style: 'none',
|
||||
name: 'demo',
|
||||
skipFormat: false,
|
||||
});
|
||||
|
||||
// Config that isn't configured properly
|
||||
tree.write(
|
||||
'apps/demo/next.config.js',
|
||||
`const withNx = require('@nx/next/plugins/with-nx');
|
||||
const nextConfig = {
|
||||
webpack5: false,
|
||||
};
|
||||
|
||||
module.exports = withNx(nextConfig);
|
||||
`
|
||||
);
|
||||
|
||||
await update(tree);
|
||||
|
||||
const result = tree.read('apps/demo/next.config.js').toString();
|
||||
|
||||
expect(result).toMatch(/webpack5: true/);
|
||||
});
|
||||
|
||||
it('should fix stylus support', async () => {
|
||||
await applicationGenerator(tree, {
|
||||
style: 'styl',
|
||||
name: 'demo',
|
||||
skipFormat: false,
|
||||
});
|
||||
|
||||
// Config that isn't configured properly
|
||||
tree.write(
|
||||
'apps/demo/next.config.js',
|
||||
`const withNx = require('@nx/next/plugins/with-nx');
|
||||
const withStylus = require('@zeit/next-stylus');
|
||||
|
||||
const nextConfig = {
|
||||
webpack5: false,
|
||||
};
|
||||
|
||||
module.exports = withNx(withStylus(nextConfig));
|
||||
`
|
||||
);
|
||||
|
||||
await update(tree);
|
||||
|
||||
const result = tree.read('apps/demo/next.config.js').toString();
|
||||
|
||||
expect(result).toMatch(/@nrwl\/next\/plugins\/with-stylus/);
|
||||
expect(result).not.toMatch(/@zeit\/next-stylus/);
|
||||
});
|
||||
|
||||
it('should fix less support', async () => {
|
||||
await applicationGenerator(tree, {
|
||||
style: 'less',
|
||||
name: 'demo',
|
||||
skipFormat: false,
|
||||
});
|
||||
|
||||
// Config that isn't configured properly
|
||||
tree.write(
|
||||
'apps/demo/next.config.js',
|
||||
`const withNx = require('@nx/next/plugins/with-nx');
|
||||
const withLess = require('@zeit/next-less');
|
||||
|
||||
const nextConfig = {
|
||||
webpack5: false,
|
||||
};
|
||||
|
||||
module.exports = withNx(withLess(nextConfig));
|
||||
`
|
||||
);
|
||||
|
||||
await update(tree);
|
||||
|
||||
const result = tree.read('apps/demo/next.config.js').toString();
|
||||
|
||||
expect(result).toMatch(/next-with-less/);
|
||||
expect(result).not.toMatch(/@zeit\/next-less/);
|
||||
});
|
||||
});
|
||||
@ -1,51 +0,0 @@
|
||||
import {
|
||||
addDependenciesToPackageJson,
|
||||
formatFiles,
|
||||
GeneratorCallback,
|
||||
getProjects,
|
||||
joinPathFragments,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
|
||||
export async function update(host: Tree) {
|
||||
const projects = getProjects(host);
|
||||
let task: undefined | GeneratorCallback = undefined;
|
||||
|
||||
projects.forEach((project) => {
|
||||
const configPath = joinPathFragments(project.root, 'next.config.js');
|
||||
|
||||
if (!host.exists(configPath)) return;
|
||||
|
||||
const content = host.read(configPath).toString();
|
||||
let updated = content.replace(/webpack5: false/, 'webpack5: true');
|
||||
|
||||
if (content.match(/@zeit\/next-less/)) {
|
||||
updated = updated.replace('@zeit/next-less', 'next-with-less');
|
||||
task = addDependenciesToPackageJson(
|
||||
host,
|
||||
{ 'next-with-less': '1.0.1' },
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
if (content.match(/@zeit\/next-stylus/)) {
|
||||
updated = updated.replace(
|
||||
'@zeit/next-stylus',
|
||||
'@nrwl/next/plugins/with-stylus'
|
||||
);
|
||||
task = addDependenciesToPackageJson(
|
||||
host,
|
||||
{ 'stylus-loader': '6.2.0' },
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
host.write(configPath, updated);
|
||||
});
|
||||
|
||||
await formatFiles(host);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
export default update;
|
||||
@ -1,40 +0,0 @@
|
||||
import {
|
||||
addDependenciesToPackageJson,
|
||||
formatFiles,
|
||||
GeneratorCallback,
|
||||
getProjects,
|
||||
joinPathFragments,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
|
||||
export async function update(host: Tree) {
|
||||
const projects = getProjects(host);
|
||||
let task: undefined | GeneratorCallback = undefined;
|
||||
|
||||
projects.forEach((project) => {
|
||||
const configPath = joinPathFragments(project.root, 'next.config.js');
|
||||
|
||||
if (!host.exists(configPath)) return;
|
||||
|
||||
const content = host.read(configPath).toString();
|
||||
|
||||
if (content.match(/next-with-less/)) {
|
||||
const updated = content.replace(
|
||||
'next-with-less',
|
||||
'@nrwl/next/plugins/with-less'
|
||||
);
|
||||
task = addDependenciesToPackageJson(
|
||||
host,
|
||||
{ 'less-loader': '10.2.0' },
|
||||
{}
|
||||
);
|
||||
host.write(configPath, updated);
|
||||
}
|
||||
});
|
||||
|
||||
await formatFiles(host);
|
||||
|
||||
return task;
|
||||
}
|
||||
|
||||
export default update;
|
||||
@ -1,203 +0,0 @@
|
||||
import {
|
||||
readProjectConfiguration,
|
||||
Tree,
|
||||
updateJson,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
|
||||
import { applicationGenerator } from '../../generators/application/application';
|
||||
import { update } from './enable-swc';
|
||||
|
||||
describe('Migration: enable SWC', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
});
|
||||
|
||||
it('should remove .babelrc file and fix jest config', async () => {
|
||||
await applicationGenerator(tree, {
|
||||
style: 'none',
|
||||
name: 'demo',
|
||||
skipFormat: false,
|
||||
swc: false,
|
||||
});
|
||||
|
||||
updateJson(tree, 'apps/demo/.babelrc', (json) => {
|
||||
json.presets[0] = '@nrwl/next/babel';
|
||||
|
||||
return json;
|
||||
});
|
||||
|
||||
// rename jest config to js as that was standard at this version of nx
|
||||
tree.delete('apps/demo/jest.config.ts');
|
||||
updateProjectConfiguration(tree, 'demo', {
|
||||
...readProjectConfiguration(tree, 'demo'),
|
||||
targets: {
|
||||
test: {
|
||||
executor: '@nrwl/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'apps/demo/jest.config.js',
|
||||
passWithNoTests: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// Config that isn't configured properly
|
||||
tree.write(
|
||||
'apps/demo/jest.config.js',
|
||||
`
|
||||
module.exports = {
|
||||
displayName: 'napp4',
|
||||
preset: '../../jest.preset.js',
|
||||
transform: {
|
||||
'^(?!.*\\\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest',
|
||||
'^.+\\\\.[tj]sx?$': 'babel-jest',
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||
coverageDirectory: '../../coverage/apps/demo',
|
||||
};
|
||||
|
||||
`
|
||||
);
|
||||
|
||||
await update(tree);
|
||||
|
||||
const result = tree.read('apps/demo/jest.config.js').toString();
|
||||
|
||||
expect(result).toMatch(`['babel-jest', { presets: ['@nrwl/next/babel'] }]`);
|
||||
|
||||
expect(tree.exists('apps/demo/.babelrc')).toBe(false);
|
||||
});
|
||||
|
||||
it('should still fix jest config when babelrc is missing', async () => {
|
||||
await applicationGenerator(tree, {
|
||||
style: 'none',
|
||||
name: 'demo',
|
||||
skipFormat: false,
|
||||
swc: true,
|
||||
});
|
||||
// rename jest config to js as that was standard at this version of nx
|
||||
tree.delete('apps/demo/jest.config.ts');
|
||||
updateProjectConfiguration(tree, 'demo', {
|
||||
...readProjectConfiguration(tree, 'demo'),
|
||||
targets: {
|
||||
test: {
|
||||
executor: '@nrwl/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'apps/demo/jest.config.js',
|
||||
passWithNoTests: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
// Config that isn't configured properly
|
||||
tree.write(
|
||||
'apps/demo/jest.config.js',
|
||||
`
|
||||
module.exports = {
|
||||
displayName: 'napp4',
|
||||
preset: '../../jest.preset.js',
|
||||
transform: {
|
||||
'^(?!.*\\\\.(js|jsx|ts|tsx|css|json)$)': '@nrwl/react/plugins/jest',
|
||||
'^.+\\\\.[tj]sx?$': 'babel-jest',
|
||||
},
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||
coverageDirectory: '../../coverage/apps/demo',
|
||||
};
|
||||
|
||||
`
|
||||
);
|
||||
|
||||
await update(tree);
|
||||
|
||||
const result = tree.read('apps/demo/jest.config.js').toString();
|
||||
|
||||
expect(result).toMatch(`['babel-jest', { presets: ['@nrwl/next/babel'] }]`);
|
||||
});
|
||||
it('should skip migration if the babelrc has been customized', async () => {
|
||||
await applicationGenerator(tree, {
|
||||
style: 'none',
|
||||
name: 'demo',
|
||||
skipFormat: false,
|
||||
swc: false,
|
||||
});
|
||||
// rename jest config to js as that was standard at this version of nx
|
||||
tree.rename('apps/demo/jest.config.ts', 'apps/demo/jest.config.js');
|
||||
updateProjectConfiguration(tree, 'demo', {
|
||||
...readProjectConfiguration(tree, 'demo'),
|
||||
targets: {
|
||||
test: {
|
||||
executor: '@nrwl/jest:jest',
|
||||
options: {
|
||||
jestConfig: 'apps/demo/jest.config.js',
|
||||
passWithNoTests: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
tree.write(
|
||||
'apps/demo/.babelrc',
|
||||
`{
|
||||
"presets": ["@nrwl/next/babel", "something-else"],
|
||||
"plugins": []
|
||||
}`
|
||||
);
|
||||
|
||||
await update(tree);
|
||||
|
||||
expect(tree.exists('apps/demo/.babelrc')).toBe(true);
|
||||
|
||||
tree.write(
|
||||
'apps/demo/.babelrc',
|
||||
`{
|
||||
"presets": ["@nrwl/next/babel"],
|
||||
"plugins": ["some-plugin"]
|
||||
}`
|
||||
);
|
||||
|
||||
await update(tree);
|
||||
|
||||
expect(tree.exists('apps/demo/.babelrc')).toBe(true);
|
||||
|
||||
// No custom plugins, can migrate.
|
||||
tree.write(
|
||||
'apps/demo/.babelrc',
|
||||
`{
|
||||
"presets": ["@nrwl/next/babel"]
|
||||
}`
|
||||
);
|
||||
|
||||
await update(tree);
|
||||
|
||||
expect(tree.exists('apps/demo/.babelrc')).toBe(false);
|
||||
});
|
||||
|
||||
it('should skip migration if storybook configuration is detected', async () => {
|
||||
await applicationGenerator(tree, {
|
||||
style: 'none',
|
||||
name: 'demo',
|
||||
skipFormat: false,
|
||||
swc: false,
|
||||
});
|
||||
|
||||
tree.write(
|
||||
'apps/demo/.babelrc',
|
||||
`{
|
||||
"presets": ["@nrwl/next/babel"]
|
||||
}`
|
||||
);
|
||||
tree.write(
|
||||
'apps/demo/.storybook/main.js',
|
||||
`module.exports = {
|
||||
stories: []
|
||||
}`
|
||||
);
|
||||
|
||||
await update(tree);
|
||||
|
||||
expect(tree.exists('apps/demo/.babelrc')).toBe(true);
|
||||
});
|
||||
});
|
||||
@ -1,62 +0,0 @@
|
||||
import {
|
||||
formatFiles,
|
||||
getProjects,
|
||||
joinPathFragments,
|
||||
logger,
|
||||
readJson,
|
||||
Tree,
|
||||
} from '@nx/devkit';
|
||||
|
||||
export async function update(host: Tree) {
|
||||
const projects = getProjects(host);
|
||||
|
||||
projects.forEach((project) => {
|
||||
const nextConfigPath = joinPathFragments(project.root, 'next.config.js');
|
||||
const jestConfigPath = joinPathFragments(project.root, 'jest.config.js');
|
||||
const babelConfigPath = joinPathFragments(project.root, '.babelrc');
|
||||
const storybookMainPath = joinPathFragments(
|
||||
project.root,
|
||||
'.storybook/main.js'
|
||||
);
|
||||
|
||||
if (!host.exists(nextConfigPath) || !host.exists(jestConfigPath)) return;
|
||||
|
||||
if (host.exists(babelConfigPath)) {
|
||||
if (customBabelConfig(host, babelConfigPath)) {
|
||||
logger.info(
|
||||
`NX Custom .babelrc file detected, skipping deletion. You can delete this file yourself to enable SWC: ${babelConfigPath}`
|
||||
);
|
||||
} else if (host.exists(storybookMainPath)) {
|
||||
logger.info(
|
||||
`NX Storybook configuration for project "${project.name}" detected, skipping deletion of .babelrc`
|
||||
);
|
||||
} else {
|
||||
// Deleting custom babel config enables SWC
|
||||
host.delete(babelConfigPath);
|
||||
}
|
||||
}
|
||||
|
||||
const content = host.read(jestConfigPath).toString();
|
||||
|
||||
if (content.match(/:\s+'babel-jest'/)) {
|
||||
const updated = content.replace(
|
||||
/:\s+'babel-jest'/,
|
||||
`: ['babel-jest', { presets: ['@nrwl/next/babel'] }]`
|
||||
);
|
||||
host.write(jestConfigPath, updated);
|
||||
}
|
||||
});
|
||||
|
||||
await formatFiles(host);
|
||||
}
|
||||
|
||||
function customBabelConfig(host, configPath) {
|
||||
const json = readJson(host, configPath);
|
||||
return !(
|
||||
json.presets?.length === 1 &&
|
||||
json.presets?.[0] === '@nrwl/next/babel' &&
|
||||
(json.plugins?.length === 0 || !json.plugins)
|
||||
);
|
||||
}
|
||||
|
||||
export default update;
|
||||
@ -1,101 +0,0 @@
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { addProjectConfiguration, readProjectConfiguration } from '@nx/devkit';
|
||||
import update from './add-default-development-configurations';
|
||||
|
||||
describe('React default development configuration', () => {
|
||||
it('should add development configuration if it does not exist', async () => {
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
addProjectConfiguration(
|
||||
tree,
|
||||
'example',
|
||||
{
|
||||
root: 'apps/example',
|
||||
projectType: 'application',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/next:build',
|
||||
configurations: {},
|
||||
},
|
||||
serve: {
|
||||
executor: '@nrwl/next:server',
|
||||
configurations: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
await update(tree);
|
||||
|
||||
const config = readProjectConfiguration(tree, 'example');
|
||||
|
||||
expect(config.targets.build.defaultConfiguration).toEqual('production');
|
||||
expect(config.targets.build.configurations.development).toEqual({});
|
||||
|
||||
expect(config.targets.serve.defaultConfiguration).toEqual('development');
|
||||
expect(config.targets.serve.configurations.development).toEqual({
|
||||
buildTarget: `example:build:development`,
|
||||
dev: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should add development configuration if no configurations at all', async () => {
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
addProjectConfiguration(
|
||||
tree,
|
||||
'example',
|
||||
{
|
||||
root: 'apps/example',
|
||||
projectType: 'application',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/next:build',
|
||||
defaultConfiguration: 'production',
|
||||
configurations: { production: {} },
|
||||
},
|
||||
serve: {
|
||||
executor: '@nrwl/next:server',
|
||||
},
|
||||
},
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
await update(tree);
|
||||
|
||||
const config = readProjectConfiguration(tree, 'example');
|
||||
|
||||
expect(config.targets.build.defaultConfiguration).toEqual('production');
|
||||
expect(config.targets.build.configurations.production).toEqual({});
|
||||
expect(config.targets.build.configurations.development).toEqual({});
|
||||
|
||||
expect(config.targets.serve.defaultConfiguration).toEqual('development');
|
||||
expect(config.targets.serve.configurations.development).toEqual({
|
||||
buildTarget: `example:build:development`,
|
||||
dev: true,
|
||||
});
|
||||
});
|
||||
|
||||
it('should work without targets', async () => {
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
addProjectConfiguration(
|
||||
tree,
|
||||
'example',
|
||||
{
|
||||
root: 'apps/example',
|
||||
projectType: 'application',
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
await update(tree);
|
||||
|
||||
const config = readProjectConfiguration(tree, 'example');
|
||||
expect(config).toEqual({
|
||||
$schema: '../../node_modules/nx/schemas/project-schema.json',
|
||||
name: 'example',
|
||||
root: 'apps/example',
|
||||
projectType: 'application',
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -1,36 +0,0 @@
|
||||
import {
|
||||
formatFiles,
|
||||
getProjects,
|
||||
Tree,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
|
||||
export async function update(tree: Tree) {
|
||||
const projects = getProjects(tree);
|
||||
|
||||
projects.forEach((config, name) => {
|
||||
let shouldUpdate = false;
|
||||
if (config.targets?.build?.executor === '@nrwl/next:build') {
|
||||
shouldUpdate = true;
|
||||
config.targets.build.defaultConfiguration ??= 'production';
|
||||
config.targets.build.configurations ??= {};
|
||||
config.targets.build.configurations.development ??= {};
|
||||
}
|
||||
|
||||
if (config.targets?.serve?.executor === '@nrwl/next:server') {
|
||||
shouldUpdate = true;
|
||||
config.targets.serve.defaultConfiguration ??= 'development';
|
||||
config.targets.serve.configurations ??= {};
|
||||
config.targets.serve.configurations.development ??= {
|
||||
buildTarget: `${name}:build:development`,
|
||||
dev: true,
|
||||
};
|
||||
}
|
||||
|
||||
if (shouldUpdate) updateProjectConfiguration(tree, name, config);
|
||||
});
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
export default update;
|
||||
@ -1,65 +0,0 @@
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { addProjectConfiguration, readProjectConfiguration } from '@nx/devkit';
|
||||
import update from './add-dev-output-path';
|
||||
|
||||
describe('React default development configuration', () => {
|
||||
it('should add output path if it does not alreayd exist', async () => {
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
addProjectConfiguration(
|
||||
tree,
|
||||
'example',
|
||||
{
|
||||
root: 'apps/example',
|
||||
sourceRoot: 'apps/example',
|
||||
projectType: 'application',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/next:build',
|
||||
configurations: {
|
||||
development: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
await update(tree);
|
||||
|
||||
const config = readProjectConfiguration(tree, 'example');
|
||||
|
||||
expect(config.targets.build.configurations.development.outputPath).toEqual(
|
||||
'tmp/apps/example'
|
||||
);
|
||||
});
|
||||
|
||||
it('should skip update if outputPath already exists for development', async () => {
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
addProjectConfiguration(
|
||||
tree,
|
||||
'example',
|
||||
{
|
||||
root: 'apps/example',
|
||||
sourceRoot: 'apps/example',
|
||||
projectType: 'application',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/next:build',
|
||||
configurations: {
|
||||
development: { outputPath: '/tmp/some/custom/path' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
await update(tree);
|
||||
|
||||
const config = readProjectConfiguration(tree, 'example');
|
||||
|
||||
expect(config.targets.build.configurations.development.outputPath).toEqual(
|
||||
'/tmp/some/custom/path'
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -1,25 +0,0 @@
|
||||
import {
|
||||
formatFiles,
|
||||
getProjects,
|
||||
joinPathFragments,
|
||||
Tree,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
|
||||
export async function update(tree: Tree) {
|
||||
const projects = getProjects(tree);
|
||||
|
||||
projects.forEach((config, name) => {
|
||||
if (config.targets?.build?.executor === '@nrwl/next:build') {
|
||||
config.targets.build.configurations ??= {};
|
||||
config.targets.build.configurations.development ??= {};
|
||||
config.targets.build.configurations.development.outputPath ??=
|
||||
joinPathFragments('tmp', config.sourceRoot);
|
||||
updateProjectConfiguration(tree, name, config);
|
||||
}
|
||||
});
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
export default update;
|
||||
@ -1,9 +0,0 @@
|
||||
import { formatFiles, Tree } from '@nx/devkit';
|
||||
import { addGitIgnoreEntry } from '../../utils/add-gitignore-entry';
|
||||
|
||||
export async function update(tree: Tree) {
|
||||
addGitIgnoreEntry(tree);
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
export default update;
|
||||
@ -1,95 +0,0 @@
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { addProjectConfiguration, readProjectConfiguration } from '@nx/devkit';
|
||||
import update from './update-dev-output-path';
|
||||
|
||||
describe('React default development configuration', () => {
|
||||
it('should add output path if it does not alreayd exist', async () => {
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
addProjectConfiguration(
|
||||
tree,
|
||||
'example',
|
||||
{
|
||||
root: 'apps/example',
|
||||
sourceRoot: 'apps/example',
|
||||
projectType: 'application',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/next:build',
|
||||
configurations: {
|
||||
development: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
await update(tree);
|
||||
|
||||
const config = readProjectConfiguration(tree, 'example');
|
||||
|
||||
expect(config.targets.build.configurations.development.outputPath).toEqual(
|
||||
'apps/example'
|
||||
);
|
||||
});
|
||||
|
||||
it('should add output path is default generated pat', async () => {
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
addProjectConfiguration(
|
||||
tree,
|
||||
'example',
|
||||
{
|
||||
root: 'apps/example',
|
||||
sourceRoot: 'apps/example',
|
||||
projectType: 'application',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/next:build',
|
||||
configurations: {
|
||||
development: { outputPath: 'tmp/apps/example' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
await update(tree);
|
||||
|
||||
const config = readProjectConfiguration(tree, 'example');
|
||||
|
||||
expect(config.targets.build.configurations.development.outputPath).toEqual(
|
||||
'apps/example'
|
||||
);
|
||||
});
|
||||
|
||||
it('should skip update if outputPath already exists for development and does not match expected path', async () => {
|
||||
const tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
addProjectConfiguration(
|
||||
tree,
|
||||
'example',
|
||||
{
|
||||
root: 'apps/example',
|
||||
sourceRoot: 'apps/example',
|
||||
projectType: 'application',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/next:build',
|
||||
configurations: {
|
||||
development: { outputPath: '.tmp/custom' },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
true
|
||||
);
|
||||
|
||||
await update(tree);
|
||||
|
||||
const config = readProjectConfiguration(tree, 'example');
|
||||
|
||||
expect(config.targets.build.configurations.development.outputPath).toEqual(
|
||||
'.tmp/custom'
|
||||
);
|
||||
});
|
||||
});
|
||||
@ -1,31 +0,0 @@
|
||||
import {
|
||||
formatFiles,
|
||||
getProjects,
|
||||
joinPathFragments,
|
||||
Tree,
|
||||
updateProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
|
||||
export async function update(tree: Tree) {
|
||||
const projects = getProjects(tree);
|
||||
|
||||
projects.forEach((config, name) => {
|
||||
if (config.targets?.build?.executor === '@nrwl/next:build') {
|
||||
config.targets.build.configurations ??= {};
|
||||
config.targets.build.configurations.development ??= {};
|
||||
if (
|
||||
!config.targets.build.configurations.development.outputPath ||
|
||||
config.targets.build.configurations.development.outputPath ===
|
||||
joinPathFragments('tmp', config.root)
|
||||
) {
|
||||
config.targets.build.configurations.development.outputPath =
|
||||
config.root;
|
||||
updateProjectConfiguration(tree, name, config);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
export default update;
|
||||
@ -1,60 +0,0 @@
|
||||
import { addProjectConfiguration, readJson, Tree } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { updateNextEslint } from './update-next-eslint';
|
||||
|
||||
describe('Add next eslint 14.5.7', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(async () => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
|
||||
addProjectConfiguration(tree, 'app1', {
|
||||
root: 'apps/app1',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/next:build',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
tree.write(
|
||||
'nx.json',
|
||||
JSON.stringify({
|
||||
projects: {
|
||||
app1: {},
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should update "next" config ignorePattern', async () => {
|
||||
tree.write(
|
||||
'apps/app1/.eslintrc.json',
|
||||
JSON.stringify({
|
||||
extends: ['../../.eslintrc.json'],
|
||||
ignorePatterns: ['!**/*'],
|
||||
})
|
||||
);
|
||||
|
||||
await updateNextEslint(tree);
|
||||
|
||||
const result = readJson(tree, 'apps/app1/.eslintrc.json');
|
||||
expect(result.ignorePatterns).toContain('.next/**/*');
|
||||
});
|
||||
|
||||
it('should not update "next" config ignorePattern if .next pattern already exists', async () => {
|
||||
tree.write(
|
||||
'apps/app1/.eslintrc.json',
|
||||
JSON.stringify({
|
||||
extends: ['../../.eslintrc.json'],
|
||||
ignorePatterns: ['!**/*', '.next/**/*', '/foo/bar'],
|
||||
})
|
||||
);
|
||||
const before = readJson(tree, 'apps/app1/.eslintrc.json');
|
||||
|
||||
await updateNextEslint(tree);
|
||||
|
||||
const result = readJson(tree, 'apps/app1/.eslintrc.json');
|
||||
expect(result.ignorePatterns).toEqual(before.ignorePatterns);
|
||||
});
|
||||
});
|
||||
@ -1,28 +0,0 @@
|
||||
import { formatFiles, getProjects, Tree, updateJson } from '@nx/devkit';
|
||||
|
||||
export async function updateNextEslint(host: Tree) {
|
||||
const projects = getProjects(host);
|
||||
|
||||
projects.forEach((project) => {
|
||||
if (project.targets?.build?.executor !== '@nrwl/next:build') return;
|
||||
|
||||
const eslintPath = `${project.root}/.eslintrc.json`;
|
||||
|
||||
if (!host.exists(eslintPath)) return;
|
||||
|
||||
updateJson(host, eslintPath, (eslintConfig) => {
|
||||
const nextIgnorePattern = '.next/**/*';
|
||||
|
||||
if (eslintConfig.ignorePatterns.indexOf(nextIgnorePattern) < 0) {
|
||||
eslintConfig.ignorePatterns = [
|
||||
...eslintConfig.ignorePatterns,
|
||||
nextIgnorePattern,
|
||||
];
|
||||
}
|
||||
return eslintConfig;
|
||||
});
|
||||
});
|
||||
await formatFiles(host);
|
||||
}
|
||||
|
||||
export default updateNextEslint;
|
||||
@ -1,35 +1,5 @@
|
||||
{
|
||||
"generators": {
|
||||
"remove-webpack-5-packages": {
|
||||
"cli": "nx",
|
||||
"version": "13.0.0-beta.1",
|
||||
"description": "Remove packages installed by Nx 12's `@nrwl/node:webpack5` generator.",
|
||||
"factory": "./src/migrations/update-13-0-0/remove-webpack-5-packages-13-0-0"
|
||||
},
|
||||
"rename-build-to-webpack": {
|
||||
"cli": "nx",
|
||||
"version": "13.8.5-beta.1",
|
||||
"description": "Renames @nrwl/node:build to @nrwl/node:webpack",
|
||||
"factory": "./src/migrations/update-13-8-5/rename-build-to-webpack"
|
||||
},
|
||||
"rename-execute-to-node": {
|
||||
"cli": "nx",
|
||||
"version": "13.8.5-beta.1",
|
||||
"description": "Renames @nrwl/node:execute to @nrwl/node:node",
|
||||
"factory": "./src/migrations/update-13-8-5/rename-execute-to-node"
|
||||
},
|
||||
"update-package-to-tsc": {
|
||||
"cli": "nx",
|
||||
"version": "13.8.5-beta.1",
|
||||
"description": "Renames @nrwl/node:package to @nrwl/js:tsc",
|
||||
"factory": "./src/migrations/update-13-8-5/update-package-to-tsc"
|
||||
},
|
||||
"update-webpack-executor": {
|
||||
"cli": "nx",
|
||||
"version": "14.7.6-beta.1",
|
||||
"description": "Update usages of webpack executors to @nrwl/webpack",
|
||||
"factory": "./src/migrations/update-14-7-6/update-webpack-executor"
|
||||
},
|
||||
"update-16-0-0-add-nx-packages": {
|
||||
"cli": "nx",
|
||||
"version": "16.0.0-beta.1",
|
||||
@ -55,24 +25,5 @@
|
||||
"implementation": "./src/migrations/update-16-4-0/replace-node-executor"
|
||||
}
|
||||
},
|
||||
"packageJsonUpdates": {
|
||||
"14.3.7": {
|
||||
"version": "14.3.7-beta.0",
|
||||
"packages": {
|
||||
"@types/node": {
|
||||
"version": "18.0.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"14.5.5": {
|
||||
"version": "14.5.5-beta.0",
|
||||
"packages": {
|
||||
"@types/node": {
|
||||
"version": "18.7.1",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"packageJsonUpdates": {}
|
||||
}
|
||||
|
||||
@ -34,8 +34,7 @@
|
||||
"@nx/devkit": "file:../devkit",
|
||||
"@nx/jest": "file:../jest",
|
||||
"@nx/js": "file:../js",
|
||||
"@nx/linter": "file:../linter",
|
||||
"@nx/workspace": "file:../workspace"
|
||||
"@nx/linter": "file:../linter"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
|
||||
@ -1,52 +0,0 @@
|
||||
import {
|
||||
addProjectConfiguration,
|
||||
readJson,
|
||||
readProjectConfiguration,
|
||||
} from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
|
||||
import subject from './remove-deprecated-options-13-0-0';
|
||||
|
||||
describe('Migration: Remove deprecated options', () => {
|
||||
it(`should remove deprecated node build options`, async () => {
|
||||
let tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
|
||||
addProjectConfiguration(tree, 'myapp', {
|
||||
root: 'apps/myapp',
|
||||
sourceRoot: 'apps/myapp/src',
|
||||
projectType: 'application',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/node:build',
|
||||
options: {
|
||||
showCircularDependencies: false,
|
||||
},
|
||||
configurations: {
|
||||
production: {
|
||||
showCircularDependencies: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await subject(tree);
|
||||
|
||||
expect(readProjectConfiguration(tree, 'myapp')).toEqual({
|
||||
$schema: '../../node_modules/nx/schemas/project-schema.json',
|
||||
name: 'myapp',
|
||||
root: 'apps/myapp',
|
||||
sourceRoot: 'apps/myapp/src',
|
||||
projectType: 'application',
|
||||
targets: {
|
||||
build: {
|
||||
executor: '@nrwl/node:build',
|
||||
options: {},
|
||||
configurations: {
|
||||
production: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
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