Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 544a100359 | |||
| db1331630a | |||
| 0b5cb5b1c1 | |||
| 5a42215007 | |||
| e075e2d6e1 | |||
| 3c8f7592b9 | |||
| b2a7927245 | |||
| 7be9aef725 | |||
| 70b977bcd6 | |||
| f45f484ee5 | |||
| 09f8a8d7d1 | |||
| 0b57d42f19 | |||
| e54535e0d3 | |||
| 55963d60fa | |||
| 9151176784 | |||
| 85a84d1d80 | |||
| 2deb3a9ae3 | |||
| ee46f990ff | |||
| 02a5f1c64e | |||
| 0a24474f77 | |||
| 6acf28e86c | |||
| b90cd236f3 | |||
| 69d0c8b435 | |||
| 38bfae2aa7 | |||
| 2bab225141 | |||
| 683a02ffff | |||
| 43f129dfbd | |||
| baaa579b39 | |||
| cb140ed260 |
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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');
|
||||
|
||||
|
||||
@@ -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,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('-');
|
||||
}
|
||||
|
||||
+100
-21
@@ -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);
|
||||
}
|
||||
|
||||
+6
-3
@@ -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),
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
};
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -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
@@ -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) {}
|
||||
@@ -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.'
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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[],
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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')
|
||||
);
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -40,6 +40,15 @@
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"16.4.1": {
|
||||
"version": "16.4.1-beta.0",
|
||||
"packages": {
|
||||
"webpack": {
|
||||
"version": "^5.80.0",
|
||||
"alwaysAddToPackageJson": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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('/')
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
Generated
+683
-548
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user