Compare commits

...

29 Commits

Author SHA1 Message Date
FrozenPandaz 544a100359 chore(misc): publish 16.4.1 2023-06-29 18:28:10 -04:00
Miroslav Jonaš db1331630a fix(core): fix lockfile remapping for yarn berry with aliases (#17853)
(cherry picked from commit cfe1fa044f)
2023-06-29 16:30:32 -04:00
Jonathan Cammisuli 0b5cb5b1c1 fix(core): improve error message when native binaries are not found for a supported platform (#17817)
(cherry picked from commit fc4b6a8f31)
2023-06-29 16:30:30 -04:00
Jonathan Cammisuli 5a42215007 fix(core): remove check for taskGraph equality (#17862)
(cherry picked from commit 9579404525)
2023-06-29 12:21:59 -04:00
Leosvel Pérez Espinosa e075e2d6e1 fix(core): pin @yarnpkg/parsers version to avoid dropping support for node.js v16 (#17860)
(cherry picked from commit b70c7f4802)
2023-06-29 12:21:58 -04:00
Caleb Ukle 3c8f7592b9 chore(repo): bump webpack version (#17836)
(cherry picked from commit 7d15e0c350)
2023-06-28 17:56:19 -04:00
Leosvel Pérez Espinosa b2a7927245 fix(core): fix race condition in task scheduler (#17837)
(cherry picked from commit 03ffa422be)
2023-06-28 17:55:24 -04:00
Leosvel Pérez Espinosa 7be9aef725 fix(angular): process only angular projects in migration (#17833)
(cherry picked from commit 20d45bbdf1)
2023-06-28 17:55:19 -04:00
Austin Fahsl 70b977bcd6 chore(misc): patch lerna 6.6.2 to catch additional publish conflicts (#17809)
(cherry picked from commit 5e51bf4041)
2023-06-28 17:55:15 -04:00
Leosvel Pérez Espinosa f45f484ee5 fix(misc): fix misc issues with move generators (#17814)
(cherry picked from commit 7055c724dc)
2023-06-28 17:55:13 -04:00
Jason Jean 09f8a8d7d1 fix(core): fix workspace validation for implicitDependencies that are… (#17825)
(cherry picked from commit c0508e38e3)
2023-06-28 17:55:10 -04:00
Craigory Coppola 0b57d42f19 fix(core): point to daemon logs when daemon closes unexpectedly (#17827)
(cherry picked from commit 6588ff9faf)
2023-06-28 17:55:08 -04:00
Emily Xiong e54535e0d3 fix(nx-plugin): pass in e2e project name create package preset (#17824)
(cherry picked from commit 2941ddc6a2)
2023-06-28 17:55:00 -04:00
Craigory Coppola 55963d60fa fix(core): cli option --graph should work with scoped package names (#17820)
(cherry picked from commit f2d7dcb331)
2023-06-28 17:54:56 -04:00
FrozenPandaz 9151176784 fix(storybook): fix migration version 2023-06-27 17:28:12 -04:00
Leosvel Pérez Espinosa 85a84d1d80 feat(storybook): update storybook version to ^7.0.24 (#17818)
(cherry picked from commit d4d48fbbd4)
2023-06-27 17:27:07 -04:00
Nicholas Cunningham 2deb3a9ae3 fix(nextjs): Custom server should run on fresh apps without errors (#17806)
(cherry picked from commit bd7157bb83)
2023-06-27 15:23:32 -04:00
Isaac Mann ee46f990ff docs(core): update deprecation messages (#17804)
(cherry picked from commit c00e2f3f6e)
2023-06-27 15:23:23 -04:00
Isaac Mann 02a5f1c64e docs(core): workspace-lint cli links to web (#17807)
(cherry picked from commit 37eda46460)
2023-06-26 18:31:35 -04:00
Craigory Coppola 0a24474f77 fix(misc): prevent unexpected targets from root package.json when setting up root project (#17805)
(cherry picked from commit c6ad657c9f)
2023-06-26 18:31:27 -04:00
Jason Jean 6acf28e86c fix(core): fix globbing in rust (#17803)
(cherry picked from commit 38101fdbf0)
2023-06-26 18:31:25 -04:00
Nicholas Cunningham b90cd236f3 fix(nextjs): KeepAliveTimeout arg (#17801)
(cherry picked from commit 47c95ae5d4)
2023-06-26 18:31:20 -04:00
Caleb Ukle 69d0c8b435 fix(webpack): use correct version for migration (#17789)
(cherry picked from commit e1db93c1b8)
2023-06-26 18:31:17 -04:00
Jack Hsu 38bfae2aa7 fix(nextjs): update workspace libs setup in weback config (#17795)
(cherry picked from commit fdf755584f)
2023-06-26 18:31:14 -04:00
Nicholas Cunningham 2bab225141 fix(nextjs): For serve executor keepAliveTimeout should be camelCased (#17792)
(cherry picked from commit cf08214889)
2023-06-26 18:31:11 -04:00
Caleb Ukle 683a02ffff fix(webpack): sync webpack version to @nx/webpack (#17707)
(cherry picked from commit 77430cbf4c)
2023-06-26 18:31:07 -04:00
Leosvel Pérez Espinosa 43f129dfbd fix(nest): do not overwrite tsconfig files with undefined in migration (#17783)
(cherry picked from commit 9195111d74)
2023-06-26 18:31:04 -04:00
Jack Hsu baaa579b39 fix(bundling): separate image and font resources so they do not conflict (#17763)
(cherry picked from commit 9c0c30edb4)
2023-06-26 18:31:00 -04:00
Steven Rathbauer cb140ed260 fix(storybook): fix 6.1.0 migration to prevent wiping out eslintrc (#17753)
(cherry picked from commit b736992968)
2023-06-26 18:30:58 -04:00
70 changed files with 1790 additions and 958 deletions
+1 -1
View File
@@ -5,7 +5,7 @@ description: 'Graph dependencies affected by changes'
# affected:graph
**Deprecated:** Use `nx graph --affected`, or` nx affected --graph` instead depending on which best suits your use case. The `affected:graph` command will be removed in Nx 18.
**Deprecated:** Use `nx graph --affected`, or `nx affected --graph` instead depending on which best suits your use case. The `affected:graph` command will be removed in Nx 18.
Graph dependencies affected by changes
+1 -1
View File
@@ -5,7 +5,7 @@ description: 'Prints information about the projects and targets affected by chan
# print-affected
**Deprecated:** Use `nx show --affected`, `nx affected --graph` or `nx graph --affected` depending on which best suits your use case. The `print-affected` command will be removed in Nx 18.
**Deprecated:** Use `nx show projects --affected`, `nx affected --graph -t build` or `nx graph --affected` depending on which best suits your use case. The `print-affected` command will be removed in Nx 18.
Prints information about the projects and targets affected by changes
+1 -1
View File
@@ -5,7 +5,7 @@ description: 'Lint nx specific workspace files (nx.json, workspace.json)'
# workspace-lint
**Deprecated:** workspace-lint is deprecated, and will be removed in v17. The checks it used to perform are no longer relevant.
**Deprecated:** workspace-lint is deprecated, and will be removed in v17. The checks it used to perform are no longer relevant. See: https://nx.dev/deprecated/workspace-lint
Lint nx specific workspace files (nx.json, workspace.json)
@@ -5,7 +5,7 @@ description: 'Graph dependencies affected by changes'
# affected:graph
**Deprecated:** Use `nx graph --affected`, or` nx affected --graph` instead depending on which best suits your use case. The `affected:graph` command will be removed in Nx 18.
**Deprecated:** Use `nx graph --affected`, or `nx affected --graph` instead depending on which best suits your use case. The `affected:graph` command will be removed in Nx 18.
Graph dependencies affected by changes
@@ -5,7 +5,7 @@ description: 'Prints information about the projects and targets affected by chan
# print-affected
**Deprecated:** Use `nx show --affected`, `nx affected --graph` or `nx graph --affected` depending on which best suits your use case. The `print-affected` command will be removed in Nx 18.
**Deprecated:** Use `nx show projects --affected`, `nx affected --graph -t build` or `nx graph --affected` depending on which best suits your use case. The `print-affected` command will be removed in Nx 18.
Prints information about the projects and targets affected by changes
@@ -5,7 +5,7 @@ description: 'Lint nx specific workspace files (nx.json, workspace.json)'
# workspace-lint
**Deprecated:** workspace-lint is deprecated, and will be removed in v17. The checks it used to perform are no longer relevant.
**Deprecated:** workspace-lint is deprecated, and will be removed in v17. The checks it used to perform are no longer relevant. See: https://nx.dev/deprecated/workspace-lint
Lint nx specific workspace files (nx.json, workspace.json)
@@ -22,7 +22,16 @@ When updating Nx that is already on 15.8, the package-lock.json should continue
### How to fix
1. If you are running your install command with `--no-optional`, try again without the flag.
2. Delete your node_modules and `package-lock.json` and re-run `npm i`. This should have the `package-lock.json` file updated properly.
1. Delete your node_modules and `package-lock.json` (or other lock files) and re-run your package manager's install command.
1. If running on Windows, make sure that the [installed Microsoft Visual C++ Redistributable is up-to-date](https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads).
Confirm that you see `@nx/nx-plaform-arch` in your `node_modules` folder (e.g. `@nx/nx-darwin-arm64`, `@nx/nx-win32-x64-msvc`, etc).
If you are still experiencing issues after following the previous steps, please [open an issue on Github](https://github.com/nrwl/nx/issues/new?assignees=&labels=type:+bug&projects=&template=1-bug.yml) and we will help you troubleshoot.
Be prepared to give as much detail as possible about your system, we will need the following information at a minimum, the contents of `nx report` plus
- Operating system version
- The package manager (npm, yarn, pnpm, etc) install command
### Supported native module platforms
+71
View File
@@ -0,0 +1,71 @@
import {
cleanupProject,
newProject,
runCLI,
uniq,
updateFile,
} from '@nx/e2e/utils';
import { checkApp } from './utils';
describe('Next.js Experimental Features', () => {
let proj: string;
beforeEach(() => {
proj = newProject();
});
afterEach(() => {
cleanupProject();
});
it('should be able to define server actions in workspace libs', async () => {
const appName = uniq('app');
const libName = uniq('lib');
runCLI(`generate @nx/next:app ${appName}`);
runCLI(`generate @nx/next:lib ${libName} --no-interactive`);
// Update the app to test two scenarios:
// 1. Workspace lib with server actions through 'use server' directive
// 2. Workspace with a client component through 'use client' directive
updateFile(
`libs/${libName}/src/lib/action.ts`,
`
'use server';
export async function addItem() {
console.log('adding item');
}
`
);
updateFile(
`libs/${libName}/src/lib/${libName}.tsx`,
`
'use client';
import { addItem } from './action';
export function TestComponent() {
return (
<form action={addItem}>
<button type="submit">Add</button>
</form>
);
};
`
);
updateFile(
`apps/${appName}/app/page.tsx`,
`
import { TestComponent } from '@proj/${libName}';
export default function Home() {
return <TestComponent />;
}
`
);
await checkApp(appName, {
checkUnitTest: false,
checkLint: true,
checkE2E: false,
checkExport: false,
});
}, 300_000);
});
Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 B

+53 -13
View File
@@ -5,16 +5,20 @@ import {
createFile,
isNotWindows,
killPorts,
listFiles,
newProject,
readFile,
rmDist,
runCLI,
runCLIAsync,
runCypressTests,
tmpProjPath,
uniq,
updateFile,
updateProjectConfig,
} from '@nx/e2e/utils';
import { join } from 'path';
import { copyFileSync } from 'fs';
describe('Web Components Applications', () => {
beforeEach(() => newProject());
@@ -29,24 +33,11 @@ describe('Web Components Applications', () => {
const lintResults = runCLI(`lint ${appName}`);
expect(lintResults).toContain('All files pass linting.');
runCLI(`build ${appName} --outputHashing none --compiler babel`);
checkFilesExist(
`dist/apps/${appName}/index.html`,
`dist/apps/${appName}/runtime.js`,
`dist/apps/${appName}/main.js`,
`dist/apps/${appName}/styles.css`
);
expect(readFile(`dist/apps/${appName}/index.html`)).toContain(
'<link rel="stylesheet" href="styles.css">'
);
const testResults = await runCLIAsync(`test ${appName}`);
expect(testResults.combinedOutput).toContain(
'Test Suites: 1 passed, 1 total'
);
const lintE2eResults = runCLI(`lint ${appName}-e2e`);
expect(lintE2eResults).toContain('All files pass linting.');
@@ -56,6 +47,55 @@ describe('Web Components Applications', () => {
expect(e2eResults).toContain('All specs passed!');
expect(await killPorts()).toBeTruthy();
}
copyFileSync(
join(__dirname, 'test-fixtures/inlined.png'),
join(tmpProjPath(), `apps/${appName}/src/app/inlined.png`)
);
copyFileSync(
join(__dirname, 'test-fixtures/emitted.png'),
join(tmpProjPath(), `apps/${appName}/src/app/emitted.png`)
);
updateFile(
`apps/${appName}/src/app/app.element.ts`,
`
// @ts-ignore
import inlined from './inlined.png';
// @ts-ignore
import emitted from './emitted.png';
export class AppElement extends HTMLElement {
public static observedAttributes = [];
connectedCallback() {
this.innerHTML = \`
<img src="\${inlined} "/>
<img src="\${emitted} "/>
\`;
}
}
customElements.define('app-root', AppElement);
`
);
runCLI(`build ${appName} --outputHashing none --compiler babel`);
checkFilesExist(
`dist/apps/${appName}/index.html`,
`dist/apps/${appName}/runtime.js`,
`dist/apps/${appName}/emitted.png`,
`dist/apps/${appName}/main.js`,
`dist/apps/${appName}/styles.css`
);
checkFilesDoNotExist(`dist/apps/${appName}/inlined.png`);
expect(readFile(`dist/apps/${appName}/main.js`)).toContain(
'<img src="data:image/png;base64'
);
// Should not be a JS module but kept as a PNG
expect(readFile(`dist/apps/${appName}/emitted.png`)).not.toContain(
'export default'
);
expect(readFile(`dist/apps/${appName}/index.html`)).toContain(
'<link rel="stylesheet" href="styles.css">'
);
}, 500000);
it('should remove previous output before building', async () => {
+1 -1
View File
@@ -1,6 +1,6 @@
{
"packages": ["build/packages/*", "build/packages/nx/native-packages/*"],
"version": "16.4.0",
"version": "16.4.1",
"granularPathspec": false,
"command": {
"publish": {
+12 -8
View File
@@ -87,11 +87,11 @@
"@rollup/plugin-node-resolve": "^13.0.4",
"@rollup/plugin-url": "^7.0.0",
"@schematics/angular": "~16.1.0",
"@storybook/addon-essentials": "^7.0.9",
"@storybook/core-server": "^7.0.9",
"@storybook/react": "^7.0.9",
"@storybook/react-webpack5": "^7.0.9",
"@storybook/types": "^7.0.9",
"@storybook/addon-essentials": "^7.0.24",
"@storybook/core-server": "^7.0.24",
"@storybook/react": "^7.0.24",
"@storybook/react-webpack5": "^7.0.24",
"@storybook/types": "^7.0.24",
"@svgr/rollup": "^8.0.1",
"@svgr/webpack": "^8.0.1",
"@swc-node/register": "^1.4.2",
@@ -267,7 +267,7 @@
"use-sync-external-store": "^1.2.0",
"verdaccio": "^5.0.4",
"vite": "^4.3.4",
"webpack": "^5.80.0",
"webpack": "5.88.0",
"webpack-dev-server": "^4.9.3",
"webpack-merge": "^5.8.0",
"webpack-node-externals": "^3.0.0",
@@ -299,7 +299,7 @@
"@tailwindcss/typography": "^0.5.7",
"@types/license-checker": "^25.0.3",
"@yarnpkg/lockfile": "^1.1.0",
"@yarnpkg/parsers": "^3.0.0-rc.18",
"@yarnpkg/parsers": "3.0.0-rc.46",
"@zkochan/js-yaml": "0.0.6",
"axios": "1.0.0",
"classnames": "^2.3.1",
@@ -347,6 +347,10 @@
"depcheck",
"documentation"
]
},
"pnpm": {
"patchedDependencies": {
"lerna@6.6.2": "patches/lerna@6.6.2.patch"
}
}
}
@@ -109,6 +109,7 @@ export function executeWebpackBrowserBuilder(
);
baseWebpackConfig.plugins.push(
// @ts-expect-error - difference between angular and webpack plugin definitions bc of webpack versions
new WebpackNxBuildCoordinationPlugin(
`nx run-many --target=${
context.target.target
@@ -129,6 +129,7 @@ export function executeWebpackDevServerBuilder(
// This will occur when workspaceDependencies = []
if (workspaceDependencies.length > 0) {
baseWebpackConfig.plugins.push(
// @ts-expect-error - difference between angular and webpack plugin definitions bc of webpack versions
new WebpackNxBuildCoordinationPlugin(
`nx run-many --target=${
parsedBrowserTarget.target
@@ -0,0 +1,4 @@
export * from './normalize-schema';
export * from './update-module-name';
export * from './update-ng-package';
export * from './update-secondary-entry-points';
@@ -0,0 +1,15 @@
import type { Tree } from '@nx/devkit';
import { readProjectConfiguration } from '@nx/devkit';
import type { NormalizedSchema, Schema } from '../schema';
import { getNewProjectName } from '../../utils/get-new-project-name';
export function normalizeSchema(tree: Tree, schema: Schema): NormalizedSchema {
const newProjectName = getNewProjectName(schema.destination);
const { root } = readProjectConfiguration(tree, schema.projectName);
return {
...schema,
newProjectName,
oldProjectRoot: root,
};
}
@@ -4,7 +4,7 @@ import { Linter } from '@nx/linter';
import { moveGenerator } from '@nx/workspace/generators';
import { UnitTestRunner } from '../../../utils/test-runners';
import { generateTestLibrary } from '../../utils/testing';
import { Schema } from '../schema';
import { NormalizedSchema } from '../schema';
import { updateModuleName } from './update-module-name';
describe('updateModuleName Rule', () => {
@@ -20,17 +20,19 @@ describe('updateModuleName Rule', () => {
name: 'my-first',
simpleName: true,
});
const schema: Schema = {
const schema: NormalizedSchema = {
projectName: 'my-first',
destination: 'my/first',
updateImportPath: true,
newProjectName: 'my-first',
oldProjectRoot: 'libs/my-first',
};
await moveGenerator(tree, schema);
updateModuleName(tree, { ...schema, destination: 'my/first' });
expect(tree.exists(updatedModulePath)).toBe(true);
const moduleFile = tree.read(updatedModulePath).toString('utf-8');
const moduleFile = tree.read(updatedModulePath, 'utf-8');
expect(moduleFile).toContain(`export class MyFirstModule {}`);
});
@@ -42,10 +44,12 @@ describe('updateModuleName Rule', () => {
const indexPath = '/libs/shared/my-first/src/index.ts';
const secondModulePath = '/libs/my-second/src/lib/my-second.module.ts';
const schema: Schema = {
const schema: NormalizedSchema = {
projectName: 'my-first',
destination: 'shared/my-first',
updateImportPath: true,
newProjectName: 'shared-my-first',
oldProjectRoot: 'libs/my-first',
};
beforeEach(async () => {
@@ -111,10 +115,10 @@ describe('updateModuleName Rule', () => {
expect(tree.exists(updatedModulePath)).toBe(true);
expect(tree.exists(updatedModuleSpecPath)).toBe(true);
const moduleFile = tree.read(updatedModulePath).toString('utf-8');
const moduleFile = tree.read(updatedModulePath, 'utf-8');
expect(moduleFile).toContain(`export class SharedMyFirstModule {}`);
const moduleSpecFile = tree.read(updatedModuleSpecPath).toString('utf-8');
const moduleSpecFile = tree.read(updatedModuleSpecPath, 'utf-8');
expect(moduleSpecFile).toContain(
`import { SharedMyFirstModule } from './shared-my-first.module';`
);
@@ -130,7 +134,7 @@ describe('updateModuleName Rule', () => {
it('should update any references to the module', async () => {
updateModuleName(tree, schema);
const importerFile = tree.read(secondModulePath).toString('utf-8');
const importerFile = tree.read(secondModulePath, 'utf-8');
expect(importerFile).toContain(
`import { SharedMyFirstModule } from '@proj/shared/my-first';`
);
@@ -142,7 +146,7 @@ describe('updateModuleName Rule', () => {
it('should update the index.ts file which exports the module', async () => {
updateModuleName(tree, schema);
const indexFile = tree.read(indexPath).toString('utf-8');
const indexFile = tree.read(indexPath, 'utf-8');
expect(indexFile).toContain(
`export * from './lib/shared-my-first.module';`
);
@@ -150,10 +154,12 @@ describe('updateModuleName Rule', () => {
});
describe('rename', () => {
const schema: Schema = {
const schema: NormalizedSchema = {
projectName: 'my-source',
destination: 'my-destination',
updateImportPath: true,
newProjectName: 'my-destination',
oldProjectRoot: 'libs/my-source',
};
const modulePath = '/libs/my-destination/src/lib/my-destination.module.ts';
@@ -233,10 +239,10 @@ describe('updateModuleName Rule', () => {
expect(tree.exists(modulePath)).toBe(true);
expect(tree.exists(moduleSpecPath)).toBe(true);
const moduleFile = tree.read(modulePath).toString('utf-8');
const moduleFile = tree.read(modulePath, 'utf-8');
expect(moduleFile).toContain(`export class MyDestinationModule {}`);
const moduleSpecFile = tree.read(moduleSpecPath).toString('utf-8');
const moduleSpecFile = tree.read(moduleSpecPath, 'utf-8');
expect(moduleSpecFile).toContain(
`import { MyDestinationModule } from './my-destination.module';`
);
@@ -252,7 +258,7 @@ describe('updateModuleName Rule', () => {
it('should update any references to the module', async () => {
updateModuleName(tree, schema);
const importerFile = tree.read(importerPath).toString('utf-8');
const importerFile = tree.read(importerPath, 'utf-8');
expect(importerFile).toContain(
`import { MyDestinationModule } from '@proj/my-destination';`
);
@@ -264,10 +270,32 @@ describe('updateModuleName Rule', () => {
it('should update the index.ts file which exports the module', async () => {
updateModuleName(tree, schema);
const indexFile = tree.read(indexPath).toString('utf-8');
const indexFile = tree.read(indexPath, 'utf-8');
expect(indexFile).toContain(
`export * from './lib/my-destination.module';`
);
});
it('should not rename unrelated symbols with similar name in different projects', async () => {
// create different project whose main module name starts with the same
// name of the project we're moving
await generateTestLibrary(tree, {
name: 'my-source-demo',
buildable: false,
linter: Linter.EsLint,
publishable: false,
simpleName: true,
skipFormat: false,
unitTestRunner: UnitTestRunner.Jest,
});
updateModuleName(tree, schema);
const moduleFile = tree.read(
'/libs/my-source-demo/src/lib/my-source-demo.module.ts',
'utf-8'
);
expect(moduleFile).toContain(`export class MySourceDemoModule {}`);
});
});
});
@@ -1,13 +1,12 @@
import {
getProjects,
joinPathFragments,
names,
readProjectConfiguration,
Tree,
visitNotIgnoredFiles,
} from '@nx/devkit';
import { getNewProjectName } from '../../utils/get-new-project-name';
import { join } from 'path';
import { Schema } from '../schema';
import type { NormalizedSchema } from '../schema';
/**
* Updates the Angular module name (including the spec file and index.ts)
@@ -19,10 +18,8 @@ import { Schema } from '../schema';
*/
export function updateModuleName(
tree: Tree,
{ projectName, destination }: Schema
{ projectName: oldProjectName, newProjectName }: NormalizedSchema
): void {
const newProjectName = getNewProjectName(destination);
const project = readProjectConfiguration(tree, newProjectName);
if (project.projectType === 'application') {
@@ -32,14 +29,14 @@ export function updateModuleName(
}
const moduleName = {
from: names(projectName).className,
to: names(newProjectName).className,
from: `${names(oldProjectName).className}Module`,
to: `${names(newProjectName).className}Module`,
};
const findModuleName = new RegExp(`\\b${moduleName.from}`, 'g');
const moduleFile = {
from: `${projectName}.module`,
from: `${oldProjectName}.module`,
to: `${newProjectName}.module`,
};
@@ -81,7 +78,7 @@ export function updateModuleName(
});
// update index file
const indexFile = join(project.sourceRoot, 'index.ts');
const indexFile = joinPathFragments(project.sourceRoot, 'index.ts');
if (tree.exists(indexFile)) {
updateFileContent(tree, replacements, indexFile);
}
@@ -6,13 +6,11 @@ import {
updateJson,
workspaceRoot,
} from '@nx/devkit';
import { getNewProjectName } from '../../utils/get-new-project-name';
import { join, relative } from 'path';
import { Schema } from '../schema';
import type { NormalizedSchema } from '../schema';
export function updateNgPackage(tree: Tree, schema: Schema): void {
const newProjectName = getNewProjectName(schema.destination);
const project = readProjectConfiguration(tree, newProjectName);
export function updateNgPackage(tree: Tree, schema: NormalizedSchema): void {
const project = readProjectConfiguration(tree, schema.newProjectName);
if (project.projectType === 'application') {
return;
@@ -29,13 +27,13 @@ export function updateNgPackage(tree: Tree, schema: Schema): void {
const outputs = getOutputsForTargetAndConfiguration(
{
target: {
project: newProjectName,
project: schema.newProjectName,
target: 'build',
},
overrides: {},
},
{
name: newProjectName,
name: schema.newProjectName,
type: 'lib',
data: {
root: project.root,
@@ -0,0 +1,71 @@
import type { Tree } from '@nx/devkit';
import {
joinPathFragments,
normalizePath,
readProjectConfiguration,
visitNotIgnoredFiles,
} from '@nx/devkit';
import { basename, dirname } from 'path';
import type { NormalizedSchema } from '../schema';
const libraryExecutors = [
'@angular-devkit/build-angular:ng-packagr',
'@nx/angular:ng-packagr-lite',
'@nx/angular:package',
// TODO(v17): remove when @nrwl/* scope is removed
'@nrwl/angular:ng-packagr-lite',
'@nrwl/angular:package',
];
export function updateSecondaryEntryPoints(
tree: Tree,
schema: NormalizedSchema
): void {
const project = readProjectConfiguration(tree, schema.newProjectName);
if (project.projectType !== 'library') {
return;
}
if (
!Object.values(project.targets ?? {}).some((target) =>
libraryExecutors.includes(target.executor)
)
) {
return;
}
visitNotIgnoredFiles(tree, project.root, (filePath) => {
if (
basename(filePath) !== 'ng-package.json' ||
normalizePath(filePath) ===
joinPathFragments(project.root, 'ng-package.json')
) {
return;
}
updateReadme(
tree,
dirname(filePath),
schema.projectName,
schema.newProjectName
);
});
}
function updateReadme(
tree: Tree,
dir: string,
oldProjectName: string,
newProjectName: string
) {
const readmePath = joinPathFragments(dir, 'README.md');
if (!tree.exists(readmePath)) {
return;
}
const findName = new RegExp(`${oldProjectName}`, 'g');
const oldContent = tree.read(readmePath, 'utf-8');
const newContent = oldContent.replace(findName, newProjectName);
tree.write(readmePath, newContent);
}
@@ -3,6 +3,7 @@ import { readJson, Tree } from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { Linter } from '@nx/linter';
import { UnitTestRunner } from '../../utils/test-runners';
import librarySecondaryEntryPointGenerator from '../library-secondary-entry-point/library-secondary-entry-point';
import { generateTestLibrary } from '../utils/testing';
import { angularMoveGenerator } from './move';
@@ -50,6 +51,28 @@ describe('@nx/angular:move', () => {
expect(ngPackageJson.dest).toEqual('../../dist/libs/mynewlib2');
});
it('should update secondary entry points readme file', async () => {
await generateTestLibrary(tree, { name: 'mylib2', buildable: true });
await librarySecondaryEntryPointGenerator(tree, {
library: 'mylib2',
name: 'testing',
});
await angularMoveGenerator(tree, {
projectName: 'mylib2',
destination: 'mynewlib2',
updateImportPath: true,
});
const readme = tree.read('libs/mynewlib2/testing/README.md', 'utf-8');
expect(readme).toMatchInlineSnapshot(`
"# @proj/mynewlib2/testing
Secondary entry point of \`@proj/mynewlib2\`. It can be used by importing from \`@proj/mynewlib2/testing\`.
"
`);
});
it('should format files', async () => {
jest.spyOn(devkit, 'formatFiles');
+14 -7
View File
@@ -1,8 +1,12 @@
import { formatFiles, Tree } from '@nx/devkit';
import { moveGenerator } from '@nx/workspace/generators';
import { updateModuleName } from './lib/update-module-name';
import { updateNgPackage } from './lib/update-ng-package';
import { Schema } from './schema';
import {
normalizeSchema,
updateModuleName,
updateNgPackage,
updateSecondaryEntryPoints,
} from './lib';
import type { Schema } from './schema';
/**
* Moves an Angular lib/app to another folder (and renames it in the process)
@@ -15,11 +19,14 @@ export async function angularMoveGenerator(
tree: Tree,
schema: Schema
): Promise<void> {
await moveGenerator(tree, { ...schema, skipFormat: true });
updateModuleName(tree, schema);
updateNgPackage(tree, schema);
const normalizedSchema = normalizeSchema(tree, schema);
if (!schema.skipFormat) {
await moveGenerator(tree, { ...schema, skipFormat: true });
updateModuleName(tree, normalizedSchema);
updateNgPackage(tree, normalizedSchema);
updateSecondaryEntryPoints(tree, normalizedSchema);
if (!normalizedSchema.skipFormat) {
await formatFiles(tree);
}
}
+5
View File
@@ -5,3 +5,8 @@ export interface Schema {
importPath?: string;
skipFormat?: boolean;
}
export interface NormalizedSchema extends Schema {
oldProjectRoot: string;
newProjectName: string;
}
@@ -1,8 +1,15 @@
import { normalizePath } from '@nx/devkit';
/**
* Replaces slashes with dashes
* Joins path segments replacing slashes with dashes
*
* @param path
*/
export function getNewProjectName(path: string): string {
return path.replace(/\//g, '-');
// strip leading '/' or './' or '../' and trailing '/' and replaces '/' with '-'
return normalizePath(path)
.replace(/(^\.{0,2}\/|\.{1,2}\/|\/$)/g, '')
.split('/')
.filter((x) => !!x)
.join('-');
}
@@ -1,6 +1,17 @@
import {
ProjectConfiguration,
ProjectGraph,
Tree,
addProjectConfiguration,
} from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import extractStandaloneConfig from './extract-standalone-config-from-bootstrap';
import { addProjectConfiguration } from '@nx/devkit';
let projectGraph: ProjectGraph;
jest.mock('@nx/devkit', () => ({
...jest.requireActual('@nx/devkit'),
createProjectGraphAsync: () => Promise.resolve(projectGraph),
}));
const TEST_MAIN_FILE = `import { bootstrapApplication } from '@angular/platform-browser';
import {
@@ -18,19 +29,22 @@ describe('extractStandaloneConfigFromBootstrap', () => {
it('should extract the config correctly from a standard main.ts file', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace();
addProjectConfiguration(tree, 'app1', {
name: 'app1',
root: 'apps/app1',
sourceRoot: 'apps/app1/src',
projectType: 'application',
targets: {
build: {
options: {
main: 'apps/app1/src/main.ts',
addProject(
tree,
'app1',
{
name: 'app1',
root: 'apps/app1',
sourceRoot: 'apps/app1/src',
projectType: 'application',
targets: {
build: {
options: { main: 'apps/app1/src/main.ts' },
},
},
},
});
['npm:@angular/core']
);
tree.write('apps/app1/src/main.ts', TEST_MAIN_FILE);
@@ -68,19 +82,22 @@ describe('extractStandaloneConfigFromBootstrap', () => {
it('should extract the config correctly when the main.ts imports bootstrap from bootstrap.ts file', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace();
addProjectConfiguration(tree, 'app1', {
name: 'app1',
root: 'apps/app1',
sourceRoot: 'apps/app1/src',
projectType: 'application',
targets: {
build: {
options: {
main: 'apps/app1/src/main.ts',
addProject(
tree,
'app1',
{
name: 'app1',
root: 'apps/app1',
sourceRoot: 'apps/app1/src',
projectType: 'application',
targets: {
build: {
options: { main: 'apps/app1/src/main.ts' },
},
},
},
});
['npm:@angular/core']
);
tree.write('apps/app1/src/main.ts', `import('./bootstrap');`);
tree.write('apps/app1/src/bootstrap.ts', TEST_MAIN_FILE);
@@ -120,4 +137,66 @@ describe('extractStandaloneConfigFromBootstrap', () => {
"
`);
});
it('should not throw with non-angular projects', async () => {
// ARRANGE
const tree = createTreeWithEmptyWorkspace();
addProject(
tree,
'app1',
{
name: 'app1',
projectType: 'application',
root: 'apps/app1',
sourceRoot: 'apps/app1',
targets: {
build: {
executor: '@nx-go/nx-go:build',
options: { main: 'apps/app1' },
},
},
},
[]
);
tree.write(
'apps/app1/main.go',
`package main
import "fmt"
func Hello(name string) string {
result := "Hello " + name
return result
}
func main() {
fmt.Println(Hello("app1"))
}`
);
// ACT && ASSERT
await expect(extractStandaloneConfig(tree)).resolves.not.toThrow();
});
});
function addProject(
tree: Tree,
projectName: string,
config: ProjectConfiguration,
dependencies: string[]
): void {
projectGraph = {
dependencies: {
[projectName]: dependencies.map((d) => ({
source: projectName,
target: d,
type: 'static',
})),
},
nodes: {
[projectName]: { data: config, name: projectName, type: 'app' },
},
};
addProjectConfiguration(tree, projectName, config);
}
@@ -1,8 +1,9 @@
import type { ProjectConfiguration, Tree } from '@nx/devkit';
import { formatFiles, getProjects, joinPathFragments } from '@nx/devkit';
import { formatFiles, joinPathFragments } from '@nx/devkit';
import { ensureTypescript } from '@nx/js/src/utils/typescript/ensure-typescript';
import { dirname, relative, resolve } from 'path';
import type { Identifier, Node, SourceFile, StringLiteral } from 'typescript';
import { getProjectsFilteredByDependencies } from '../utils/projects';
let tsModule: typeof import('typescript');
let tsquery: typeof import('@phenomnomnominal/tsquery').tsquery;
@@ -163,14 +164,16 @@ export default async function extractStandaloneConfig(tree: Tree) {
tsquery = require('@phenomnomnominal/tsquery').tsquery;
}
const projects = getProjects(tree);
const projects = await getProjectsFilteredByDependencies(tree, [
'npm:@angular/core',
]);
const BOOTSTRAP_APPLICATION_CALL_SELECTOR =
'CallExpression:has(Identifier[name=bootstrapApplication])';
const BOOTSTRAP_APPLICATION_CALL_CONFIG_SELECTOR =
'CallExpression:has(Identifier[name=bootstrapApplication]) > ObjectLiteralExpression';
for (const [, project] of projects.entries()) {
for (const { project } of projects) {
if (project.projectType !== 'application') {
continue;
}
@@ -17,11 +17,10 @@ export async function getProjectsFilteredByDependencies(
const projectGraph = await createProjectGraphAsync();
return Object.entries(projectGraph.dependencies)
.filter(([node, dep]) =>
dep.some(
({ target }) =>
!projectGraph.externalNodes?.[node] && dependencies.includes(target)
)
.filter(
([node, deps]) =>
!projectGraph.externalNodes?.[node] &&
deps.some(({ target }) => dependencies.includes(target))
)
.map(([projectName]) => ({
project: readProjectConfiguration(tree, projectName),
+12 -2
View File
@@ -73,9 +73,19 @@ export async function* nodeExecutor(
// Re-map buildable workspace projects to their output directory.
const mappings = calculateResolveMappings(context, options);
let outputFileName =
buildOptions.outputFileName || `${path.parse(buildOptions.main).name}.js`;
const outputFileName =
buildOptions.outputFileName ?? `${path.parse(buildOptions.main).name}.js`;
if (!buildOptions.outputFileName) {
const matches = buildOptions.main.match(/^(?!.*src)(.*)\/([^/]*)$/); //ignore strings that contain src and split paths into [folders before main, main.ts]
if (matches) {
const [mainFolder, mainFileName] = matches.slice(1);
outputFileName = path.join(
mainFolder,
`${path.parse(mainFileName).name}.js`
);
}
}
const fileToRun = join(context.root, buildOptions.outputPath, outputFileName);
const tasks: ActiveTask[] = [];
@@ -1,5 +1,5 @@
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { Tree, readJson, readProjectConfiguration } from '@nx/devkit';
import { Tree, readJson, updateJson } from '@nx/devkit';
import generator from './generator';
import { SetupVerdaccioGeneratorSchema } from './schema';
@@ -30,6 +30,49 @@ describe('setup-verdaccio generator', () => {
},
},
});
const packageJson = readJson<PackageJson>(tree, 'package.json');
expect(packageJson.nx).toEqual({
includedScripts: [],
});
});
it('should not override existing root project settings from package.json', async () => {
updateJson(tree, 'package.json', (json) => {
json.nx = {
includedScripts: ['test'],
targets: {
build: {
outputs: ['dist'],
},
},
};
return json;
});
await generator(tree, options);
const config = readJson(tree, 'project.json');
expect(config).toEqual({
name: 'test-name',
$schema: 'node_modules/nx/schemas/project-schema.json',
targets: {
'local-registry': {
executor: '@nx/js:verdaccio',
options: {
port: 4873,
config: '.verdaccio/config.yml',
storage: 'tmp/local-registry/storage',
},
},
},
});
const packageJson = readJson<PackageJson>(tree, 'package.json');
expect(packageJson.nx).toEqual({
includedScripts: ['test'],
targets: {
build: {
outputs: ['dist'],
},
},
});
});
it('should add local-registry target to project.json', async () => {
@@ -37,6 +37,14 @@ export async function setupVerdaccio(
};
if (!tree.exists('project.json')) {
const { name } = readJson(tree, 'package.json');
updateJson(tree, 'package.json', (json) => {
if (!json.nx) {
json.nx = {
includedScripts: [],
};
}
return json;
});
addProjectConfiguration(tree, name, {
root: '.',
targets: {
@@ -113,7 +113,7 @@ export function updateCacheManagerImport(
export function updateTsConfigTarget(tree: Tree, tsConfigPath: string) {
updateJson(tree, tsConfigPath, (json) => {
if (!json.compilerOptions.target) {
return;
return json;
}
const normalizedTargetName = json.compilerOptions.target.toUpperCase();
-1
View File
@@ -46,7 +46,6 @@
"semver": "7.5.3",
"ts-node": "10.9.1",
"tsconfig-paths": "^4.1.2",
"tsconfig-paths-webpack-plugin": "4.0.0",
"url-loader": "^4.1.1",
"webpack-merge": "^5.8.0",
"@nx/devkit": "file:../devkit",
+1 -3
View File
@@ -210,9 +210,7 @@ function withNx(
workspaceRoot,
projectDirectory,
options.fileReplacements,
options.assets,
dependencies,
path.join(workspaceRoot, context.libsDir)
options.assets
)(userWebpackConfig ? userWebpackConfig(a, b) : a, b);
return nextConfig;
@@ -45,7 +45,11 @@ export default async function* serveExecutor(
// Setting port that the custom server should use.
process.env.PORT = `${options.port}`;
const args = createCliOptions({ port, keepAliveTimeout, hostname });
const args = createCliOptions({ port, hostname });
if (keepAliveTimeout && !options.dev) {
args.push(`--keepAliveTimeout=${keepAliveTimeout}`);
}
const nextDir = resolve(context.root, buildOptions.outputPath);
@@ -1,7 +0,0 @@
module.exports = (phase, config, context) => {
return {
...config,
myPhase: phase,
myCustomValue: context.options.customValue,
};
};
-78
View File
@@ -1,78 +0,0 @@
import 'nx/src/utils/testing/mock-fs';
import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
import { createWebpackConfig } from './config';
jest.mock('@nx/webpack', () => ({}));
jest.mock('tsconfig-paths-webpack-plugin');
jest.mock('next/dist/server/config', () => ({
__esModule: true,
default: () => ({
webpack: () => ({}),
}),
}));
describe('Next.js webpack config builder', () => {
beforeEach(() => {
(TsconfigPathsPlugin as any).mockClear();
});
describe('createWebpackConfig', () => {
it('should set the resolve plugins', () => {
const webpackConfig = createWebpackConfig('/root', 'apps/wibble', []);
webpackConfig(
{ resolve: { alias: {} }, module: { rules: [] }, plugins: [] },
{ defaultLoaders: {} }
);
expect(TsconfigPathsPlugin).toHaveBeenCalledWith({
configFile: expect.stringMatching(/tsconfig/),
extensions: ['.ts', '.tsx', '.mjs', '.js', '.jsx'],
mainFields: ['es2015', 'module', 'main'],
});
});
it('should set the resolve aliases', () => {
const webpackConfig = createWebpackConfig('/root', 'apps/wibble', [
{
replace: 'apps/wibble/src/environment.ts',
with: 'apps/wibble/src/environment.prod.ts',
},
]);
const config = webpackConfig(
{ resolve: { alias: {} }, module: { rules: [] }, plugins: [] },
{ defaultLoaders: {} }
);
expect(config.resolve.alias).toEqual({
'/root/apps/wibble/src/environment.ts':
'/root/apps/wibble/src/environment.prod.ts',
});
});
it('should add rules for ts', () => {
const webpackConfig = createWebpackConfig('/root', 'apps/wibble', []);
const config = webpackConfig(
{
resolve: { alias: {} },
module: {
rules: [
{
test: /\.*.ts/,
loader: 'some-ts-loader',
},
],
},
plugins: [],
},
{ defaultLoaders: {} }
);
// not much value in checking what they are
// just check they get added
expect(config.module.rules.length).toBe(2);
});
});
});
+2 -59
View File
@@ -1,13 +1,8 @@
import type { NextConfig } from 'next';
import { join, resolve } from 'path';
import { TsconfigPathsPlugin } from 'tsconfig-paths-webpack-plugin';
import { resolve } from 'path';
import { Configuration, RuleSetRule } from 'webpack';
import { FileReplacement } from './types';
import { createCopyPlugin } from './create-copy-plugin';
import {
createTmpTsConfig,
DependentBuildableProjectNode,
} from '@nx/js/src/utils/buildable-libs-utils';
export interface NextConfigFn {
(phase: string, context?: any): Promise<NextConfig> | NextConfig;
@@ -25,9 +20,7 @@ export function createWebpackConfig(
workspaceRoot: string,
projectRoot: string,
fileReplacements: FileReplacement[] = [],
assets: any = null,
dependencies: DependentBuildableProjectNode[] = [],
libsDir = ''
assets: any = null
): (a, b) => Configuration {
return function webpackConfig(
config: Configuration,
@@ -39,26 +32,6 @@ export function createWebpackConfig(
isServer: boolean;
}
): Configuration {
const mainFields = ['es2015', 'module', 'main'];
const extensions = ['.ts', '.tsx', '.mjs', '.js', '.jsx'];
let tsConfigPath = join(workspaceRoot, projectRoot, 'tsconfig.json');
if (dependencies.length > 0) {
tsConfigPath = createTmpTsConfig(
tsConfigPath,
workspaceRoot,
projectRoot,
dependencies
);
}
config.resolve.plugins = [
new TsconfigPathsPlugin({
configFile: tsConfigPath,
extensions,
mainFields,
}) as never, // TODO: Remove never type when 'tsconfig-paths-webpack-plugin' types fixed
];
fileReplacements
.map((fileReplacement) => ({
replace: resolve(workspaceRoot, fileReplacement.replace),
@@ -69,25 +42,6 @@ export function createWebpackConfig(
return alias;
}, config.resolve.alias);
// Apply any rules that work on ts files to the libsDir as well
const rulesToAdd = [];
for (const r of config.module.rules) {
if (typeof r === 'string') {
continue;
}
if (isTsRule(r)) {
rulesToAdd.push({ ...r, include: [libsDir] });
} else if (r.oneOf && r.oneOf.find(isTsRule)) {
rulesToAdd.push({
...r,
oneOf: r.oneOf
.filter(isTsRule)
.map((subRule) => ({ ...subRule, include: [libsDir] })),
});
}
}
config.module.rules.push(...rulesToAdd);
// Copy (shared) assets to `public` folder during client-side compilation
if (!isServer && Array.isArray(assets) && assets.length > 0) {
config.plugins.push(createCopyPlugin(assets, workspaceRoot, projectRoot));
@@ -96,14 +50,3 @@ export function createWebpackConfig(
return config;
};
}
function isTsRule(r: RuleSetRule): boolean {
if (typeof r === 'string') {
return false;
}
if (!(r.test instanceof RegExp)) {
return false;
}
return r.test.test('a.ts');
}
+3 -19
View File
@@ -15,22 +15,15 @@ import { stripIndents } from '../src/utils/strip-indents';
import { readModulePackageJson } from '../src/utils/package-json';
import { execSync } from 'child_process';
import { join } from 'path';
import { assertSupportedPlatform } from '../src/native/assert-supported-platform';
function main() {
if (
process.argv[2] !== 'report' &&
process.argv[2] !== '--version' &&
process.argv[2] !== '--help' &&
!_supportedPlatform()
process.argv[2] !== '--help'
) {
output.error({
title: 'Platform not supported',
bodyLines: [
`This platform (${process.platform}-${process.arch}) is currently not supported by Nx.`,
'For a list of supported platforms, please see https://nx.dev/recipes/ci/troubleshoot-nx-install-issues#supported-native-module-platform',
],
});
process.exit(1);
assertSupportedPlatform();
}
const workspace = findWorkspaceRoot(process.cwd());
@@ -256,15 +249,6 @@ function _getLatestVersionOfNx(): string {
}
}
function _supportedPlatform(): boolean {
try {
require('../src/native');
return true;
} catch {
return false;
}
}
const getLatestVersionOfNx = ((fn: () => string) => {
let cache: string = null;
return () => cache || (cache = fn());
@@ -3,10 +3,13 @@ import { workspaceRoot } from '../src/utils/workspace-root';
import { fileExists } from '../src/utils/fileutils';
import { join } from 'path';
import { daemonClient } from '../src/daemon/client/client';
import { assertSupportedPlatform } from '../src/native/assert-supported-platform';
(async () => {
try {
if (isMainNxPackage() && fileExists(join(workspaceRoot, 'nx.json'))) {
assertSupportedPlatform();
try {
await daemonClient.stop();
} catch (e) {}
+2 -2
View File
@@ -9,7 +9,7 @@
"directory": "packages/nx"
},
"scripts": {
"postinstall": "node ./bin/compute-project-graph"
"postinstall": "node ./bin/post-install"
},
"keywords": [
"Monorepo",
@@ -34,7 +34,7 @@
"dependencies": {
"@parcel/watcher": "2.0.4",
"@yarnpkg/lockfile": "^1.1.0",
"@yarnpkg/parsers": "^3.0.0-rc.18",
"@yarnpkg/parsers": "3.0.0-rc.46",
"@zkochan/js-yaml": "0.0.6",
"axios": "^1.0.0",
"chalk": "^4.1.0",
@@ -106,7 +106,7 @@ export const yargsAffectedE2ECommand: CommandModule = {
};
export const affectedGraphDeprecationMessage =
'Use `nx graph --affected`, or` nx affected --graph` instead depending on which best suits your use case. The `affected:graph` command will be removed in Nx 18.';
'Use `nx graph --affected`, or `nx affected --graph` instead depending on which best suits your use case. The `affected:graph` command will be removed in Nx 18.';
/**
* @deprecated 'Use `nx graph --affected`, or` nx affected --graph` instead depending on which best suits your use case. The `affected:graph` command will be removed in Nx 18.'
*/
@@ -129,7 +129,7 @@ export const yargsAffectedGraphCommand: CommandModule = {
};
export const printAffectedDeprecationMessage =
'Use `nx show --affected`, `nx affected --graph` or `nx graph --affected` depending on which best suits your use case. The `print-affected` command will be removed in Nx 18.';
'Use `nx show projects --affected`, `nx affected --graph -t build` or `nx graph --affected` depending on which best suits your use case. The `print-affected` command will be removed in Nx 18.';
/**
* @deprecated 'Use `nx show --affected`, `nx affected --graph` or `nx graph --affected` depending on which best suits your use case. The `print-affected` command will be removed in Nx 18.'
*/
+1 -3
View File
@@ -400,9 +400,7 @@ export async function generateGraph(
} else if (args.projects) {
url.searchParams.append(
'projects',
args.projects
.map((projectName) => encodeURIComponent(projectName))
.join(' ')
args.projects.map((projectName) => projectName).join(' ')
);
} else if (args.affected) {
url.pathname += '/affected';
@@ -7,7 +7,7 @@ export const yargsWorkspaceLintCommand: CommandModule = {
command: 'workspace-lint [files..]',
describe: 'Lint nx specific workspace files (nx.json, workspace.json)',
deprecated:
'workspace-lint is deprecated, and will be removed in v17. The checks it used to perform are no longer relevant.',
'workspace-lint is deprecated, and will be removed in v17. The checks it used to perform are no longer relevant. See: https://nx.dev/deprecated/workspace-lint',
handler: async () => {
await (await import('./workspace-lint')).workspaceLint();
process.exit(0);
+1
View File
@@ -238,6 +238,7 @@ export class DaemonClient {
title: 'Daemon process terminated and closed the connection',
bodyLines: [
'Please rerun the command, which will restart the daemon.',
`If you get this error again, check for any errors in the daemon process logs found in: ${DAEMON_OUTPUT_LOG_FILE}`,
],
});
process.exit(1);
@@ -25,10 +25,7 @@ export async function handleHashTasks(payload: {
await getCachedSerializedProjectGraphPromise();
const nxJson = readNxJson();
if (
projectGraph !== storedProjectGraph ||
payload.taskGraph !== storedTaskGraph
) {
if (projectGraph !== storedProjectGraph) {
storedProjectGraph = projectGraph;
storedTaskGraph = payload.taskGraph;
storedHasher = new InProcessTaskHasher(
@@ -0,0 +1,40 @@
import { output } from '../utils/output';
export function assertSupportedPlatform() {
try {
require('./index.js');
} catch (e) {
let title = '';
let bodyLines = [];
if (
process.platform == 'win32' ||
process.platform == 'darwin' ||
process.platform == 'linux' ||
process.platform == 'freebsd'
) {
title = 'Missing Platform Dependency';
bodyLines = [
`The Nx CLI could not find or load the native binary for your supported platform (${process.platform}-${process.arch}).`,
'This likely means that optional dependencies were not installed correctly, or your system is missing some system dependencies.',
];
if (process.env.NX_VERBOSE_LOGGING == 'true') {
bodyLines.push('', 'Additional error information:', e.message);
}
} else {
title = 'Platform not supported';
bodyLines = [
`This platform (${process.platform}-${process.arch}) is currently not supported by Nx.`,
];
}
bodyLines.push(
'For more information please see https://nx.dev/recipes/ci/troubleshoot-nx-install-issues'
);
output.error({
title,
bodyLines,
});
process.exit(1);
}
}
+10 -3
View File
@@ -5,6 +5,8 @@ use std::thread::available_parallelism;
use crossbeam_channel::{unbounded, Receiver};
use ignore::WalkBuilder;
use crate::native::utils::glob::build_glob_set;
pub fn nx_walker<P, Fn, Re>(directory: P, f: Fn) -> Re
where
P: AsRef<Path>,
@@ -13,8 +15,12 @@ where
{
let directory = directory.as_ref();
let nx_ignore = directory.join(".nxignore");
let git_folder = directory.join(".git");
let node_folder = directory.join("node_modules");
let ignore_glob_set = build_glob_set(vec![
String::from("**/node_modules"),
String::from("**/.git"),
])
.expect("These static ignores always build");
let mut walker = WalkBuilder::new(directory);
walker.hidden(false);
@@ -22,7 +28,8 @@ where
// We should make sure to always ignore node_modules and the .git folder
walker.filter_entry(move |entry| {
!(entry.path().starts_with(&git_folder) || entry.path().starts_with(&node_folder))
let path = entry.path().to_string_lossy();
!ignore_glob_set.is_match(path.as_ref())
});
let cpus = available_parallelism().map_or(2, |n| n.get()) - 1;
+38 -4
View File
@@ -1,10 +1,44 @@
use globset::{Glob, GlobSet, GlobSetBuilder};
use globset::{GlobBuilder, GlobSet, GlobSetBuilder};
pub(crate) fn build_glob_set(globs: Vec<String>) -> anyhow::Result<GlobSet> {
let mut glob_builder = GlobSetBuilder::new();
let mut glob_set_builder = GlobSetBuilder::new();
for glob in globs {
glob_builder.add(Glob::new(&glob).map_err(anyhow::Error::from)?);
let glob = GlobBuilder::new(&glob)
.literal_separator(true)
.build()
.map_err(anyhow::Error::from)?;
glob_set_builder.add(glob);
}
glob_builder.build().map_err(anyhow::Error::from)
glob_set_builder.build().map_err(anyhow::Error::from)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn should_detect_package_json() {
let glob_set = build_glob_set(vec![String::from("packages/*/package.json")]).unwrap();
assert!(glob_set.is_match("packages/nx/package.json"))
}
#[test]
fn should_not_detect_deeply_nested_package_json() {
let glob_set = build_glob_set(vec![String::from("packages/*/package.json")]).unwrap();
assert!(!glob_set.is_match("packages/nx/test-files/package.json"))
}
#[test]
fn should_detect_deeply_nested_package_json() {
let glob_set = build_glob_set(vec![String::from("packages/**/package.json")]).unwrap();
assert!(glob_set.is_match("packages/nx/test-files/package.json"))
}
#[test]
fn should_detect_node_modules() {
let glob_set = build_glob_set(vec![String::from("**/node_modules")]).unwrap();
assert!(glob_set.is_match("node_modules"));
assert!(glob_set.is_match("packages/nx/node_modules"));
}
}
@@ -362,7 +362,10 @@ function mapSnapshots(
.slice(0, key.indexOf('#'))
.replace(`@patch:${packageName}@`, '@npm:');
}
if (!existingKeys.get(packageName).has(normalizedKey)) {
if (
!existingKeys.get(packageName) ||
!existingKeys.get(packageName).has(normalizedKey)
) {
keysSet.delete(key);
}
}
+15 -10
View File
@@ -25,10 +25,9 @@ export class TasksSchedule {
private reverseTaskDeps = calculateReverseDeps(this.taskGraph);
private reverseProjectGraph = reverse(this.projectGraph);
private scheduledBatches: Batch[] = [];
private scheduledTasks: string[] = [];
private completedTasks = new Set<string>();
private scheduleRequestsExecutionChain = Promise.resolve();
constructor(
private readonly hasher: TaskHasher,
@@ -40,14 +39,9 @@ export class TasksSchedule {
) {}
public async scheduleNextTasks() {
if (process.env.NX_BATCH_MODE === 'true') {
await this.scheduleBatches();
}
for (let root of this.notScheduledTaskGraph.roots) {
if (this.canBeScheduled(root)) {
await this.scheduleTask(root);
}
}
this.scheduleRequestsExecutionChain =
this.scheduleRequestsExecutionChain.then(() => this.scheduleTasks());
await this.scheduleRequestsExecutionChain;
}
public hasTasks() {
@@ -83,6 +77,17 @@ export class TasksSchedule {
: null;
}
private async scheduleTasks() {
if (process.env.NX_BATCH_MODE === 'true') {
await this.scheduleBatches();
}
for (let root of this.notScheduledTaskGraph.roots) {
if (this.canBeScheduled(root)) {
await this.scheduleTask(root);
}
}
}
private async scheduleTask(taskId: string) {
const task = this.taskGraph.tasks[taskId];
@@ -30,17 +30,18 @@ describe('assertWorkspaceValidity', () => {
it('should throw for an invalid project-level implicit dependency', () => {
mockWorkspaceJson.projects.app2.implicitDependencies = ['invalidproj'];
mockWorkspaceJson.projects.lib1.implicitDependencies = '*';
try {
assertWorkspaceValidity(mockWorkspaceJson, {});
fail('should not reach');
} catch (e) {
expect(e.message).toContain(
'The following implicitDependencies point to non-existent project(s)'
);
expect(e.message).toContain('invalidproj');
expect(e.message).toContain('invalidproj');
}
expect(() => assertWorkspaceValidity(mockWorkspaceJson, {}))
.toThrowErrorMatchingInlineSnapshot(`
"Configuration Error
The following implicitDependencies should be an array of strings:
lib1.implicitDependencies: "*"
The following implicitDependencies point to non-existent project(s):
app2
invalidproj"
`);
});
it('should throw for an invalid project-level implicit dependency with glob', () => {
@@ -38,10 +38,26 @@ export function assertWorkspaceValidity(
});
}
const projectsWithNonArrayImplicitDependencies = new Map<string, unknown>();
projectNames
.filter((projectName) => {
const project = projects[projectName];
return !!project.implicitDependencies;
// Report if for whatever reason, a project is configured to use implicitDependencies but it is not an array
if (
!!project.implicitDependencies &&
!Array.isArray(project.implicitDependencies)
) {
projectsWithNonArrayImplicitDependencies.set(
projectName,
project.implicitDependencies
);
}
return (
!!project.implicitDependencies &&
Array.isArray(project.implicitDependencies)
);
})
.reduce((map, projectName) => {
const project = projects[projectName];
@@ -55,19 +71,38 @@ export function assertWorkspaceValidity(
return map;
}, invalidImplicitDependencies);
if (invalidImplicitDependencies.size === 0) {
if (
projectsWithNonArrayImplicitDependencies.size === 0 &&
invalidImplicitDependencies.size === 0
) {
// No issues
return;
}
let message = `The following implicitDependencies point to non-existent project(s):\n`;
message += [...invalidImplicitDependencies.keys()]
.map((key) => {
const projectNames = invalidImplicitDependencies.get(key);
return ` ${key}\n${projectNames
.map((projectName) => ` ${projectName}`)
.join('\n')}`;
})
.join('\n\n');
let message = '';
if (projectsWithNonArrayImplicitDependencies.size > 0) {
message += `The following implicitDependencies should be an array of strings:\n`;
projectsWithNonArrayImplicitDependencies.forEach(
(implicitDependencies, projectName) => {
message += ` ${projectName}.implicitDependencies: "${implicitDependencies}"\n`;
}
);
message += '\n';
}
if (invalidImplicitDependencies.size > 0) {
message += `The following implicitDependencies point to non-existent project(s):\n`;
message += [...invalidImplicitDependencies.keys()]
.map((key) => {
const projectNames = invalidImplicitDependencies.get(key);
return ` ${key}\n${projectNames
.map((projectName) => ` ${projectName}`)
.join('\n')}`;
})
.join('\n\n');
}
throw new Error(`Configuration Error\n${message}`);
}
@@ -36,6 +36,7 @@ export default async function (tree: Tree, options: PresetGeneratorSchema) {
if (options.createPackageName) {
const cliTask = await createPackageGenerator(tree, {
name: options.createPackageName,
e2eProject: 'e2e',
project: options.pluginName,
skipFormat: true,
unitTestRunner: 'jest',
+3 -3
View File
@@ -135,7 +135,7 @@ const fixBabelConfigurationIfNeeded = (
let babelRuleItem;
for (const rule of webpackConfig.module.rules) {
if (typeof rule === 'string') continue;
if (!Array.isArray(rule.use)) continue;
if (!rule || !Array.isArray(rule.use)) continue;
for (const item of rule.use) {
if (typeof item !== 'string' && item['loader'].includes('babel-loader')) {
babelRuleItem = item;
@@ -229,8 +229,8 @@ export const webpack = async (
new DefinePlugin(
getClientEnvironment(storybookWebpackConfig.mode).stringified
),
...(storybookWebpackConfig.plugins ?? []),
...(finalConfig.plugins ?? [])
...((storybookWebpackConfig.plugins as WebpackPluginInstance[]) ?? []),
...((finalConfig.plugins as WebpackPluginInstance[]) ?? [])
) as WebpackPluginInstance[],
};
};
+1
View File
@@ -13,6 +13,7 @@ function addHotReload(config: Configuration) {
// add `react-refresh/babel` to babel loader plugin
const babelLoader = config.module.rules.find(
(rule) =>
rule &&
typeof rule !== 'string' &&
rule.loader?.toString().includes('babel-loader')
);
+108
View File
@@ -62,6 +62,114 @@
}
},
"packageJsonUpdates": {
"16.4.1": {
"version": "16.4.1-beta.0",
"requires": {
"@storybook/core-server": ">=7.0.0 <7.0.24"
},
"packages": {
"@storybook/core-server": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/angular": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/react": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/web-components-vite": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/web-components-webpack5": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/builder-vite": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/builder-webpack5": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/addon-a11y": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/addon-actions": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/addon-backgrounds": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/addon-controls": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/addon-docs": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/addon-essentials": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/addon-mdx-gfm": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/addon-highlight": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/addon-interactions": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/addon-jest": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/addon-links": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/addon-measure": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/addon-outline": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/addon-storyshots": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/addon-storyshots-puppeteer": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/addon-storysource": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/addon-toolbars": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
},
"@storybook/addon-viewport": {
"version": "^7.0.24",
"alwaysAddToPackageJson": false
}
}
},
"16.1.3": {
"version": "16.1.3-beta.0",
"requires": {
@@ -10,10 +10,10 @@ exports[`@nx/storybook:init dependencies for package.json should add angular rel
"devDependencies": {
"@angular/forms": "*",
"@nx/js": "0.0.1",
"@nx/storybook": "^7.0.9",
"@storybook/addon-essentials": "7.0.9",
"@storybook/angular": "7.0.9",
"@storybook/core-server": "7.0.9",
"@nx/storybook": "^7.0.24",
"@storybook/addon-essentials": "7.0.24",
"@storybook/angular": "7.0.24",
"@storybook/core-server": "7.0.24",
"existing": "1.0.0",
"prettier": "^2.6.2",
"typescript": "~5.1.3",
@@ -40,18 +40,37 @@ describe('Ignore @nx/react/plugins/storybook in Storybook eslint plugin', () =>
);
if (!tree.exists('.eslintrc.json')) {
tree.write('.eslintrc.json', '{}');
tree.write('.eslintrc.json', `{}`);
}
updateJson(tree, '.eslintrc.json', (json) => {
json.extends ??= [];
json.extends.push('plugin:storybook/recommended');
return json;
});
});
it('should not ignore the plugin if it is not used', async () => {
await eslintIgnoreReactPlugin(tree);
const eslintConfig = readJson(tree, '.eslintrc.json');
expect(eslintConfig).toEqual({});
expect(eslintConfig.rules).toBeUndefined();
});
it('should not ignore the plugin if "plugin:storybook/recommended" is not included', async () => {
tree.write('apps/main-webpack/tsconfig.json', `{}`);
tree.write(
`apps/main-webpack/.storybook/main.js`,
`
module.exports = {
stories: ['../src/lib/**/*.stories.@(mdx|js|jsx|ts|tsx)'],
addons: ['@storybook/addon-essentials', '@nx/react/plugins/storybook'],
framework: {
name: '@storybook/react-webpack5',
options: {},
},
};
`
);
await eslintIgnoreReactPlugin(tree);
const eslintConfig = readJson(tree, '.eslintrc.json');
expect(eslintConfig).toEqual({});
expect(eslintConfig.rules).toBeUndefined();
});
@@ -71,6 +90,12 @@ describe('Ignore @nx/react/plugins/storybook in Storybook eslint plugin', () =>
`
);
updateJson(tree, '.eslintrc.json', (json) => {
json.extends ??= [];
json.extends.push('plugin:storybook/recommended');
return json;
});
await eslintIgnoreReactPlugin(tree);
const eslintConfig = readJson(tree, '.eslintrc.json');
+1 -1
View File
@@ -5,6 +5,6 @@ export const storybookTestRunnerVersion = '^0.7.2';
export const litVersion = '^2.6.1';
export const tsNodeVersion = '10.9.1';
export const storybookVersion = '^7.0.9';
export const storybookVersion = '^7.0.24';
export const reactVersion = '^18.2.0';
export const viteVersion = '^4.3.4';
+9
View File
@@ -40,6 +40,15 @@
"alwaysAddToPackageJson": false
}
}
},
"16.4.1": {
"version": "16.4.1-beta.0",
"packages": {
"webpack": {
"version": "^5.80.0",
"alwaysAddToPackageJson": false
}
}
}
}
}
-1
View File
@@ -39,7 +39,6 @@
"css-loader": "^6.4.0",
"css-minimizer-webpack-plugin": "^5.0.0",
"dotenv": "~10.0.0",
"file-loader": "^6.2.0",
"fork-ts-checker-webpack-plugin": "7.2.13",
"ignore": "^5.0.4",
"less": "4.1.3",
+23 -5
View File
@@ -409,20 +409,38 @@ export function withWeb(pluginOptions: WithWebOptions = {}): NxWebpackPlugin {
...config.module,
rules: [
...(config.module.rules ?? []),
// Images: Inline small images, and emit a separate file otherwise.
{
test: /\.(bmp|png|jpe?g|gif|webp|avif)$/,
test: /\.(avif|bmp|gif|ico|jpe?g|png|webp)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 10_000, // 10 kB
},
},
generator: {
filename: `[name]${hashFormat.file}[ext]`,
},
},
// SVG: same as image but we need to separate it so it can be swapped for SVGR in the React plugin.
{
test: /\.(eot|svg|cur|jpg|png|webp|gif|otf|ttf|woff|woff2|ani)$/,
loader: require.resolve('file-loader'),
options: {
name: `[name]${hashFormat.file}.[ext]`,
test: /\.svg$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 10_000, // 10 kB
},
},
generator: {
filename: `[name]${hashFormat.file}[ext]`,
},
},
// Fonts: Emit separate file and export the URL.
{
test: /\.(eot|otf|ttf|woff|woff2)$/,
type: 'asset/resource',
generator: {
filename: `[name]${hashFormat.file}[ext]`,
},
},
...rules,
@@ -46,6 +46,25 @@ describe('normalizeSchema', () => {
expect(result).toEqual(expected);
});
it('should normalize destination and derive projectName correctly', () => {
const expected: NormalizedSchema = {
destination: 'my/library',
importPath: '@proj/my/library',
newProjectName: 'my-library',
projectName: 'my-library',
relativeToRootDestination: 'libs/my/library',
updateImportPath: true,
};
const result = normalizeSchema(
tree,
{ ...schema, destination: './my/library' },
projectConfiguration
);
expect(result).toEqual(expected);
});
it('should use provided import path', () => {
const expected: NormalizedSchema = {
destination: 'my/library',
@@ -1,6 +1,10 @@
import { ProjectConfiguration, Tree } from '@nx/devkit';
import type { NormalizedSchema, Schema } from '../schema';
import { getDestination, getNewProjectName, normalizeSlashes } from './utils';
import {
getDestination,
getNewProjectName,
normalizePathSlashes,
} from './utils';
import { getImportPath } from '../../../utilities/get-import-path';
export function normalizeSchema(
@@ -8,9 +12,7 @@ export function normalizeSchema(
schema: Schema,
projectConfiguration: ProjectConfiguration
): NormalizedSchema {
const destination = schema.destination.startsWith('/')
? normalizeSlashes(schema.destination.slice(1))
: schema.destination;
const destination = normalizePathSlashes(schema.destination);
const newProjectName =
schema.newProjectName ?? getNewProjectName(destination);
@@ -18,7 +20,8 @@ export function normalizeSchema(
...schema,
destination,
importPath:
schema.importPath ?? normalizeSlashes(getImportPath(tree, destination)),
schema.importPath ??
normalizePathSlashes(getImportPath(tree, destination)),
newProjectName,
relativeToRootDestination: getDestination(
tree,
@@ -1,4 +1,9 @@
import { readJson, readProjectConfiguration, Tree } from '@nx/devkit';
import {
readJson,
readProjectConfiguration,
Tree,
updateJson,
} from '@nx/devkit';
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
import { Schema } from '../schema';
import { updateImports } from './update-imports';
@@ -292,6 +297,37 @@ export MyExtendedClass extends MyClass {};`
});
});
it('should update project ref in the root tsconfig.base.json for secondary entry points', async () => {
await libraryGenerator(tree, {
name: 'my-source',
});
updateJson(tree, '/tsconfig.base.json', (json) => {
json.compilerOptions.paths['@proj/my-source/testing'] = [
'libs/my-source/testing/src/index.ts',
];
json.compilerOptions.paths['@proj/different-alias'] = [
'libs/my-source/some-path/src/index.ts',
];
return json;
});
const projectConfig = readProjectConfiguration(tree, 'my-source');
updateImports(
tree,
normalizeSchema(tree, schema, projectConfig),
projectConfig
);
const tsConfig = readJson(tree, '/tsconfig.base.json');
expect(tsConfig.compilerOptions.paths).toEqual({
'@proj/my-destination': ['libs/my-destination/src/index.ts'],
'@proj/my-destination/testing': [
'libs/my-destination/testing/src/index.ts',
],
'@proj/different-alias': ['libs/my-destination/some-path/src/index.ts'],
});
});
it('should update project ref of a project not under libs in the root tsconfig.base.json', async () => {
tree.delete('libs');
await libraryGenerator(tree, {
@@ -17,7 +17,7 @@ import {
findNodes,
} from '../../../utilities/ts-config';
import { NormalizedSchema } from '../schema';
import { normalizeSlashes } from './utils';
import { normalizePathSlashes } from './utils';
import { relative } from 'path';
import { ensureTypescript } from '../../../utilities/typescript';
import { getImportPath } from '../../../utilities/get-import-path';
@@ -39,88 +39,119 @@ export function updateImports(
return;
}
const { npmScope, libsDir } = getWorkspaceLayout(tree);
const { libsDir } = getWorkspaceLayout(tree);
const projects = getProjects(tree);
// use the source root to find the from location
// this attempts to account for libs that have been created with --importPath
const tsConfigPath = getRootTsConfigPathInTree(tree);
let tsConfig: any;
let fromPath: string;
let mainEntryPointImportPath: string;
let secondaryEntryPointImportPaths: string[];
if (tree.exists(tsConfigPath)) {
tsConfig = readJson(tree, tsConfigPath);
const sourceRoot =
project.sourceRoot ?? joinPathFragments(project.root, 'src');
fromPath = Object.keys(tsConfig.compilerOptions.paths).find((path) =>
mainEntryPointImportPath = Object.keys(
tsConfig.compilerOptions?.paths ?? {}
).find((path) =>
tsConfig.compilerOptions.paths[path].some((x) =>
x.startsWith(project.sourceRoot)
x.startsWith(ensureTrailingSlash(sourceRoot))
)
);
secondaryEntryPointImportPaths = Object.keys(
tsConfig.compilerOptions?.paths ?? {}
).filter((path) =>
tsConfig.compilerOptions.paths[path].some(
(x) =>
x.startsWith(ensureTrailingSlash(project.root)) &&
!x.startsWith(ensureTrailingSlash(sourceRoot))
)
);
}
const projectRef = {
from:
fromPath ||
normalizeSlashes(
getImportPath(
tree,
project.root.slice(libsDir.length).replace(/^\/|\\/, '')
)
),
to: schema.importPath,
};
mainEntryPointImportPath ??= normalizePathSlashes(
getImportPath(
tree,
project.root.slice(libsDir.length).replace(/^\/|\\/, '')
)
);
if (schema.updateImportPath) {
const replaceProjectRef = new RegExp(projectRef.from, 'g');
const projectRefs = [
{
from: mainEntryPointImportPath,
to: schema.importPath,
},
...secondaryEntryPointImportPaths.map((p) => ({
from: p,
// if the import path doesn't start with the main entry point import path,
// it's a custom import path we don't know how to update the name, we keep
// it as-is, but we'll update the path it points to
to: p.startsWith(mainEntryPointImportPath)
? p.replace(mainEntryPointImportPath, schema.importPath)
: null,
})),
];
for (const [name, definition] of Array.from(projects.entries())) {
if (name === schema.projectName) {
continue;
}
for (const projectRef of projectRefs) {
if (schema.updateImportPath && projectRef.to) {
const replaceProjectRef = new RegExp(projectRef.from, 'g');
visitNotIgnoredFiles(tree, definition.root, (file) => {
const contents = tree.read(file, 'utf-8');
replaceProjectRef.lastIndex = 0;
if (!replaceProjectRef.test(contents)) {
return;
for (const [name, definition] of Array.from(projects.entries())) {
if (name === schema.projectName) {
continue;
}
updateImportPaths(tree, file, projectRef.from, projectRef.to);
});
}
}
visitNotIgnoredFiles(tree, definition.root, (file) => {
const contents = tree.read(file, 'utf-8');
replaceProjectRef.lastIndex = 0;
if (!replaceProjectRef.test(contents)) {
return;
}
const projectRoot = {
from: project.root,
to: schema.relativeToRootDestination,
};
if (tsConfig) {
const path = tsConfig.compilerOptions.paths[projectRef.from] as string[];
if (!path) {
throw new Error(
[
`unable to find "${projectRef.from}" in`,
`${tsConfigPath} compilerOptions.paths`,
].join(' ')
);
}
const updatedPath = path.map((x) =>
joinPathFragments(projectRoot.to, relative(projectRoot.from, x))
);
if (schema.updateImportPath) {
tsConfig.compilerOptions.paths[projectRef.to] = updatedPath;
if (projectRef.from !== projectRef.to) {
delete tsConfig.compilerOptions.paths[projectRef.from];
updateImportPaths(tree, file, projectRef.from, projectRef.to);
});
}
}
const projectRoot = {
from: project.root,
to: schema.relativeToRootDestination,
};
if (tsConfig) {
const path = tsConfig.compilerOptions.paths[projectRef.from] as string[];
if (!path) {
throw new Error(
[
`unable to find "${projectRef.from}" in`,
`${tsConfigPath} compilerOptions.paths`,
].join(' ')
);
}
const updatedPath = path.map((x) =>
joinPathFragments(projectRoot.to, relative(projectRoot.from, x))
);
if (schema.updateImportPath && projectRef.to) {
tsConfig.compilerOptions.paths[projectRef.to] = updatedPath;
if (projectRef.from !== projectRef.to) {
delete tsConfig.compilerOptions.paths[projectRef.from];
}
} else {
tsConfig.compilerOptions.paths[projectRef.from] = updatedPath;
}
} else {
tsConfig.compilerOptions.paths[projectRef.from] = updatedPath;
}
writeJson(tree, tsConfigPath, tsConfig);
}
}
function ensureTrailingSlash(path: string): string {
return path.endsWith('/') ? path : `${path}/`;
}
/**
* Changes imports in a file from one import to another
*/
@@ -1,10 +1,10 @@
import {
getWorkspaceLayout,
joinPathFragments,
normalizePath,
ProjectConfiguration,
Tree,
} from '@nx/devkit';
import { Schema } from '../schema';
/**
@@ -35,12 +35,17 @@ export function getDestination(
}
/**
* Replaces slashes with dashes
* Joins path segments replacing slashes with dashes
*
* @param path
*/
export function getNewProjectName(path: string): string {
return path.replace(/\//g, '-');
// strip leading '/' or './' or '../' and trailing '/' and replaces '/' with '-'
return normalizePath(path)
.replace(/(^\.{0,2}\/|\.{1,2}\/|\/$)/g, '')
.split('/')
.filter((x) => !!x)
.join('-');
}
/**
@@ -48,9 +53,13 @@ export function getNewProjectName(path: string): string {
*
* @param input
*/
export function normalizeSlashes(input: string): string {
return input
.split('/')
.filter((x) => !!x)
.join('/');
export function normalizePathSlashes(input: string): string {
return (
normalizePath(input)
// strip leading ./ or /
.replace(/^\.?\//, '')
.split('/')
.filter((x) => !!x)
.join('/')
);
}
+48
View File
@@ -0,0 +1,48 @@
diff --git a/dist/cli.js b/dist/cli.js
index 8cd49ff1e99dca8241a75bc412c669a85475fce2..8f081b7ee0769a493b11f22c5d5d0752e8285629 100755
--- a/dist/cli.js
+++ b/dist/cli.js
@@ -7933,7 +7933,10 @@ var require_src10 = __commonJS({
logPacked(pkg.packed);
return pkg;
}).catch((err) => {
- if (err.code === "EPUBLISHCONFLICT") {
+ if (
+ err.code === "EPUBLISHCONFLICT" ||
+ (err.code === "E403" &&
+ err.body?.error?.includes("You cannot publish over the previously published versions"))) {
tracker.warn("publish", `Package is already published: ${pkg.name}@${pkg.version}`);
tracker.completeWork(1);
return pkg;
diff --git a/dist/commands/publish/index.js b/dist/commands/publish/index.js
index 34fe8f496af5e7adaf58a7f9c4c3a11d812608c3..bbab4d70c044729b174208b2f1250f54ab69e56d 100644
--- a/dist/commands/publish/index.js
+++ b/dist/commands/publish/index.js
@@ -6094,7 +6094,10 @@ var require_src2 = __commonJS({
logPacked(pkg.packed);
return pkg;
}).catch((err) => {
- if (err.code === "EPUBLISHCONFLICT") {
+ if (
+ err.code === "EPUBLISHCONFLICT" ||
+ (err.code === "E403" &&
+ err.body?.error?.includes("You cannot publish over the previously published versions"))) {
tracker.warn("publish", `Package is already published: ${pkg.name}@${pkg.version}`);
tracker.completeWork(1);
return pkg;
diff --git a/dist/index.js b/dist/index.js
index a1f83a569598efc7f129d8de7d004f45177514f4..86364db7a267c0fa3eaafc9b1bc51838a41f4f4f 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -7932,7 +7932,10 @@ var require_src10 = __commonJS({
logPacked(pkg2.packed);
return pkg2;
}).catch((err) => {
- if (err.code === "EPUBLISHCONFLICT") {
+ if (
+ err.code === "EPUBLISHCONFLICT" ||
+ (err.code === "E403" &&
+ err.body?.error?.includes("You cannot publish over the previously published versions"))) {
tracker.warn("publish", `Package is already published: ${pkg2.name}@${pkg2.version}`);
tracker.completeWork(1);
return pkg2;
+683 -548
View File
File diff suppressed because it is too large Load Diff