Compare commits

...

48 Commits

Author SHA1 Message Date
Jason Jean bf05fc3c33 chore(misc): publish 14.8.5 2022-10-13 21:21:54 -04:00
Jason Jean 8f6eb817aa fix(core): do not run postinstall unless it is the main nx package (#12591)
(cherry picked from commit 0961cb34d8)
2022-10-13 21:09:55 -04:00
Emily Marigold Klassen 731f8394f0 fix(core): drain stdout before exit in print-affected (#12559)
Piping a large print-affected output to another command, such as cat or jq, resulted in broken json
output as the node process can exit before the output is fully written. The process.stdout stream
needs to wait to drain before process.exit.

(cherry picked from commit 0ff93dd5f1)
2022-10-13 19:57:04 -04:00
Jack Hsu 64103afab1 fix(js): check if asset create/update event contains a file before copying (#12551)
(cherry picked from commit 136e1a81fe)
2022-10-13 19:56:37 -04:00
Miroslav Jonaš 9ce268b8a7 fix(core): custom generators help should print relevant information (#12258)
(cherry picked from commit f9e57e2694)
2022-10-13 19:56:21 -04:00
Miroslav Jonaš acd5964aec fix(core): readRawWorkspaceJson should keep workspace cache up to date (#12528)
(cherry picked from commit ad7945365f)
2022-10-13 19:56:19 -04:00
Craigory Coppola bb05d802a6 fix(core): do not fallback to angular cli for ng deploy (#12401)
(cherry picked from commit 74e898dd16)
2022-10-13 19:56:16 -04:00
Jason Jean d77b26173b chore(misc): publish 14.8.4 2022-10-11 13:37:58 -04:00
Victor Savkin 7865e6a91c fix(core): daemon termination should be ok when client does not need anything (#12519)
(cherry picked from commit 8c393b5660)
2022-10-11 12:27:42 -04:00
Craigory Coppola 26fce5b185 fix(core): hide leaky error message from git show command in affected (#12521)
(cherry picked from commit f37c334ea4)
2022-10-11 12:27:42 -04:00
Colum Ferry 38509cce5d fix(angular): ng-add migration should set default project (#12513)
* fix(angular): ng-add migration should set default project

* fix(angular): add pr suggestions

Co-authored-by: Leosvel Pérez Espinosa <leosvel.perez.espinosa@gmail.com>

Co-authored-by: Leosvel Pérez Espinosa <leosvel.perez.espinosa@gmail.com>
(cherry picked from commit 66a086413b)
2022-10-11 12:27:42 -04:00
Denis Frenademetz 640cfbaad6 fix(misc): update axios to 1.0.0 (#12437)
(cherry picked from commit c3abe07e68)
2022-10-11 12:27:42 -04:00
Colum Ferry 3569a6f158 fix(angular): export generators correctly #12434 (#12460)
(cherry picked from commit 32189d59b2)
2022-10-11 12:27:42 -04:00
Philip Fulcher 4a30fcd04a fix(graph): close tooltips when panning graph (#12468)
(cherry picked from commit 305fb2e4e0)
2022-10-11 12:27:42 -04:00
Benjamin Cabanes 94890f408f chore(graph): enable dark mode for scrollbars (#12430)
(cherry picked from commit ce632aa567)
2022-10-11 12:27:42 -04:00
Jack Hsu 8ba237eada fix(bundling): fix esbuild build watch (#12448)
(cherry picked from commit e0f79113dc)
2022-10-07 18:07:03 -04:00
Jack Hsu 2405a2e278 fix(bundling): do not add types field in package.json for esbuild (#12424) 2022-10-05 12:33:00 -04:00
Jason Jean c30d39a028 chore(misc): publish 14.8.3 2022-10-04 11:01:36 -04:00
Victor Savkin 60f4aca4a1 fix(core): use node based hasher in git repos with submodules
(cherry picked from commit 678f786769)
2022-10-04 10:49:34 -04:00
Victor Savkin f71730397d Revert "fix(nx): git-hasher should fetch files from git submodules (#11334)"
This reverts commit 65e5e148db.

(cherry picked from commit f016c81d94)
2022-10-04 10:49:32 -04:00
Jack Hsu ea42c9fe43 feat(bundling): add skipValidation option when generating rollup/webpack project (#12392)
(cherry picked from commit f5e35e186b)
2022-10-04 10:49:26 -04:00
Jack Hsu 5c1cc201cc cleanup(nextjs): re-enable storybook e2e test for next.js (#12384)
(cherry picked from commit 17cef57656)
2022-10-04 10:49:21 -04:00
Jack Hsu df3cd667d4 fix(nextjs): return correct webpack config for next.js storybook app (#12371)
(cherry picked from commit 5ae5f8858d)
2022-10-04 10:49:17 -04:00
Benjamin Cabanes a109b295ae chore(repo): update nx to 14.8.1 (#12383)
(cherry picked from commit 63601d4cf2)
2022-10-04 10:49:15 -04:00
Victor Savkin 8fd0f5ee0f chore(repo): update nx to latest
(cherry picked from commit 342b1e605b)
2022-10-04 10:49:12 -04:00
Victor Savkin ce0fe45de0 feat(misc): a/b different messages during migration to next major
(cherry picked from commit 77b57b78e8)
2022-10-03 19:11:23 -04:00
Miroslav Jonaš f54dd1a9f6 fix(core): disable broken git hasher recourse submodules (#12337)
(cherry picked from commit 2f0d61f2dd)
2022-10-03 19:10:50 -04:00
Daniel Harvey c3f853a21b fix(core): remove trailing slash in outputs that prevents correct caching (#10708)
Fix by removing trailing slash when performing `cp -a` into cache.

Add test to confirm fix.

Resolves nrwl/nx#10549

(cherry picked from commit 2889372dde)
2022-10-03 19:10:47 -04:00
Jack Hsu 2f2c7d8c17 fix(bundling): set NODE_ENV correctly to avoid warnings (#12331)
(cherry picked from commit c3e060247c)
2022-10-03 19:10:45 -04:00
Jack Hsu 77337691e2 fix(bundling): correct main field in package.json when using esbuild (#12328)
(cherry picked from commit a14646a864)
2022-10-03 19:10:39 -04:00
Jason Jean d294c770d0 chore(misc): publish 14.8.2 2022-09-29 18:34:52 -04:00
Rami Hanano c54e13669c fix(nx): git-hasher should fetch files from git submodules (#11334)
* fix: git-hasher should fetch files from git submodules

* chore(repo): format

* fix(git-hasher): get untracked files by calling git submodule foreach

* fix(git-hasher): get untracked files in both parent repo and submodules

* chore(git-hasher): format focument

* fix(git-hasher): fix formatting

* fix(core): make git-hasher read all files from parent repo and nested submodules

* fix(core): call git submodule foreach for unstaged files

Co-authored-by: Craigory Coppola <craigorycoppola@gmail.com>
(cherry picked from commit 65e5e148db)
2022-09-29 17:24:39 -04:00
Victor Savkin 18a7eab104 cleanup(misc): do not show links to video courses after create-nx-workspace
(cherry picked from commit fbe38865c1)
2022-09-29 17:11:42 -04:00
Victor Savkin 1dcfec1677 fix(core): daemon should not shut down due to inactivity when there are open connections
(cherry picked from commit 759f7ced86)
2022-09-29 17:11:29 -04:00
Wenchen Li 0b0131ef6c fix(webpack): regression from #10432 to allow // prefix in remote (#12302)
(cherry picked from commit ba657da7c5)
2022-09-29 17:11:24 -04:00
Phillip Barta 87735cbe6d cleanup(web): reexport WebpackNxBuildCoordinationPlugin from @nrwl/webpack (#12315)
(cherry picked from commit a592af1eb3)
2022-09-29 17:11:11 -04:00
Phillip Barta 7addb44270 fix(core): add missing core plugins to list command (#12305)
(cherry picked from commit 776b5a02f3)
2022-09-29 17:11:05 -04:00
Phillip Barta 6c289f807e cleanup(repo): remove unused dependency mime (#12254)
(cherry picked from commit 418cbc9bd0)
2022-09-29 17:10:58 -04:00
Phillip Barta 7bde32f548 fix(core): add missing packages to report command (#12306)
(cherry picked from commit 6a7a7c14a4)
2022-09-29 17:10:55 -04:00
Katerina Skroumpelou c344a1051d fix(storybook): check root mainjs for builder (#12308)
(cherry picked from commit 27037cbaed)
2022-09-29 17:10:49 -04:00
Jason Jean e7e7ba79f0 chore(repo): update lerna to 5.5.4 (#12300)
(cherry picked from commit 3c6bff2a0f)
2022-09-29 17:10:39 -04:00
Jason Jean 81f8e8c287 chore(repo): update nx to 14.8.0 (#12297)
(cherry picked from commit 5372b4b3ad)
2022-09-29 17:10:37 -04:00
Jason Jean 8b305d0232 fix(misc): remove remaining usages of @nrwl/workspace:run-commands executor (#12299) 2022-09-28 19:30:46 -04:00
Jason Jean b9ea773682 chore(misc): publish 14.8.1 2022-09-28 18:53:14 -04:00
Jason Jean 35082c7388 fix(devkit): ensure that getProjects works properly without a nx.json (#12295) 2022-09-28 17:54:35 -04:00
Victor Savkin d070fdfee8 fix(misc): buildable libs should not throw when some npm packages are not defined 2022-09-28 17:54:35 -04:00
Jack Hsu 65dc805957 fix(bundling): add missing assets ignore option to @nrwl/esbuild:esbuild (#12288) 2022-09-28 17:54:35 -04:00
Jack Hsu ac4cf513c9 fix(core): add missing packages (esbuild, webpack, rollup) to nx-migration field (#12285) 2022-09-28 11:09:35 -04:00
85 changed files with 2255 additions and 2439 deletions
+10
View File
@@ -185,6 +185,11 @@
"output": {
"type": "string",
"description": "Relative path within the output folder."
},
"ignore": {
"description": "An array of globs to ignore.",
"type": "array",
"items": { "type": "string" }
}
},
"additionalProperties": false,
@@ -278,6 +283,11 @@
"output": {
"type": "string",
"description": "Relative path within the output folder."
},
"ignore": {
"description": "An array of globs to ignore.",
"type": "array",
"items": { "type": "string" }
}
},
"additionalProperties": false,
+5
View File
@@ -82,6 +82,11 @@
"default": false,
"description": "Do not add dependencies to `package.json`."
},
"skipValidation": {
"type": "boolean",
"default": false,
"description": "Do not perform any validation on existing project."
},
"importPath": {
"type": "string",
"description": "The library name used to import it, like `@myorg/my-awesome-lib`."
+5
View File
@@ -87,6 +87,11 @@
"default": false,
"description": "Do not add dependencies to `package.json`."
},
"skipValidation": {
"type": "boolean",
"default": false,
"description": "Do not perform any validation on existing project."
},
"devServer": {
"type": "boolean",
"description": "Add a serve target to run a local webpack dev-server",
+1 -1
View File
@@ -68,7 +68,7 @@ If you are only planning to use incremental builds to speed up your CI, then the
## Custom Serve Target
If you are implementing a custom serve command, you can use `WebpackNxBuildCoordinationPlugin` provided by `@nrwl/web`. It's a webpack plugin you can use to coordinate the compiling of the libs and the webpack linking.
If you are implementing a custom serve command, you can use `WebpackNxBuildCoordinationPlugin` provided by `@nrwl/webpack`. It's a webpack plugin you can use to coordinate the compiling of the libs and the webpack linking.
## Using Webpack Module Federation to implement incremental builds
+1
View File
@@ -148,6 +148,7 @@ describe('convert Angular CLI workspace to an Nx workspace', () => {
cli: {
packageManager: packageManager,
},
defaultProject: project,
implicitDependencies: {
'.eslintrc.json': '*',
'package.json': {
+5 -1
View File
@@ -13,7 +13,9 @@ import {
describe('EsBuild Plugin', () => {
let proj: string;
beforeEach(() => (proj = newProject()));
afterEach(() => cleanupProject());
it('should setup and build projects using build', async () => {
@@ -30,8 +32,10 @@ describe('EsBuild Plugin', () => {
runCLI(`build ${myPkg}`);
expect(runCommand(`node dist/libs/${myPkg}/index.js`)).toMatch(/Hello/);
// main field should be set correctly in package.json
checkFilesExist(`dist/libs/${myPkg}/package.json`);
expect(runCommand(`node dist/libs/${myPkg}`)).toMatch(/Hello/);
expect(readFile(`dist/libs/${myPkg}/assets/a.md`)).toMatch(/file a/);
expect(readFile(`dist/libs/${myPkg}/assets/b.md`)).toMatch(/file b/);
+40
View File
@@ -0,0 +1,40 @@
import {
checkFilesExist,
cleanupProject,
newProject,
runCLI,
uniq,
} from '@nrwl/e2e/utils';
describe('Next.js Applications', () => {
let proj: string;
beforeEach(
() =>
(proj = newProject({
name: 'proj',
packageManager: 'npm',
}))
);
afterEach(() => cleanupProject());
it('should run a Next.js based Storybook setup', async () => {
const appName = uniq('app');
runCLI(`generate @nrwl/next:app ${appName} --no-interactive`);
runCLI(
`generate @nrwl/next:component Foo --project=${appName} --no-interactive`
);
// Currently due to auto-installing peer deps in pnpm, the generator can fail while installing deps with unmet peet deps.
runCLI(
`generate @nrwl/react:storybook-configuration ${appName} --generateStories --no-interactive`,
{
silenceError: true,
}
);
runCLI(`build-storybook ${appName}`);
checkFilesExist(`dist/storybook/${appName}/index.html`);
}, 1_000_000);
});
+1 -6
View File
@@ -1,14 +1,12 @@
import {
rmDist,
checkFilesExist,
cleanupProject,
expectJestTestsToPass,
isNotWindows,
killPorts,
newProject,
promisifiedTreeKill,
readFile,
readJson,
rmDist,
runCLI,
runCLIAsync,
runCommandUntil,
@@ -414,9 +412,6 @@ describe('Next.js Applications', () => {
checkExport: false,
});
}, 300_000);
it('should run default jest tests', async () => {
await expectJestTestsToPass('@nrwl/next:app');
}, 100_000);
});
function getData(port: number, path = ''): Promise<any> {
+45 -3
View File
@@ -1,4 +1,5 @@
import {
checkFilesExist,
cleanupProject,
isNotWindows,
newProject,
@@ -139,7 +140,7 @@ describe('Extra Nx Misc Tests', () => {
it('should pass options', async () => {
updateProjectConfig(mylib, (config) => {
config.targets.echo = {
executor: '@nrwl/workspace:run-commands',
executor: 'nx:run-commands',
options: {
command: 'echo --var1={args.var1}',
var1: 'a',
@@ -156,7 +157,7 @@ describe('Extra Nx Misc Tests', () => {
const echoTarget = uniq('echo');
updateProjectConfig(mylib, (config) => {
config.targets[echoTarget] = {
executor: '@nrwl/workspace:run-commands',
executor: 'nx:run-commands',
options: {
commands: [
'echo "Arguments:"',
@@ -191,7 +192,7 @@ describe('Extra Nx Misc Tests', () => {
it('ttt should fail when a process exits non-zero', async () => {
updateProjectConfig(mylib, (config) => {
config.targets.error = {
executor: '@nrwl/workspace:run-commands',
executor: 'nx:run-commands',
options: {
command: `exit 1`,
},
@@ -221,5 +222,46 @@ describe('Extra Nx Misc Tests', () => {
})
).not.toThrow();
}, 1000000);
it('should handle caching output directories containing trailing slashes', async () => {
// this test relates to https://github.com/nrwl/nx/issues/10549
// 'cp -a /path/dir/ dest/' operates differently to 'cp -a /path/dir dest/'
// --> which means actual build works but subsequent populate from cache (using cp -a) does not
// --> the fix is to remove trailing slashes to ensure consistent & expected behaviour
const mylib = uniq('lib');
const folder = `dist/libs/${mylib}/some-folder`;
runCLI(`generate @nrwl/workspace:lib ${mylib}`);
runCLI(
`generate @nrwl/workspace:run-commands build --command=echo --outputs=${folder}/ --project=${mylib}`
);
const commands = [
process.platform === 'win32'
? `mkdir ${folder}` // Windows
: `mkdir -p ${folder}`,
`echo dummy > ${folder}/dummy.txt`,
];
updateProjectConfig(mylib, (config) => {
delete config.targets.build.options.command;
config.targets.build.options = {
...config.targets.build.options,
parallel: false,
commands: commands,
};
return config;
});
// confirm that it builds correctly
runCLI(`build ${mylib}`);
checkFilesExist(`${folder}/dummy.txt`);
// confirm that it populates correctly from the cache
runCLI(`build ${mylib}`);
checkFilesExist(`${folder}/dummy.txt`);
}, 120000);
});
});
+14 -6
View File
@@ -24,9 +24,7 @@ describe('Workspace Tests', () => {
proj = newProject();
});
afterAll(() => {
cleanupProject();
});
afterAll(() => cleanupProject());
describe('@nrwl/workspace:library', () => {
it('should create a library that can be tested and linted', async () => {
@@ -195,6 +193,10 @@ describe('Workspace Tests', () => {
type: 'boolean',
description: 'skip changes to tsconfig',
};
json.properties['inlineprop'] = json.properties['name'];
json.required = ['inlineprop'];
delete json.properties['name'];
updateFile(
`tools/generators/${custom}/schema.json`,
JSON.stringify(json)
@@ -205,10 +207,17 @@ describe('Workspace Tests', () => {
`tools/generators/${custom}/index.ts`,
indexFile.replace(
'name: schema.name',
'name: schema.name, directory: schema.directory, skipTsConfig: schema.skipTsConfig'
'name: schema.inlineprop, directory: schema.directory, skipTsConfig: schema.skipTsConfig'
)
);
const helpOutput = runCLI(`workspace-generator ${custom} --help`);
expect(helpOutput).toContain(
`workspace-generator ${custom} [inlineprop] (options)`
);
expect(helpOutput).toContain(`--directory`);
expect(helpOutput).toContain(`--skipTsConfig`);
const workspace = uniq('workspace');
const dryRunOutput = runCLI(
`workspace-generator ${custom} ${workspace} --no-interactive --directory=dir --skipTsConfig=true -d`
@@ -218,11 +227,10 @@ describe('Workspace Tests', () => {
`CREATE libs/dir/${workspace}/src/index.ts`
);
const output = runCLI(
runCLI(
`workspace-generator ${custom} ${workspace} --no-interactive --directory=dir`
);
checkFilesExist(`libs/dir/${workspace}/src/index.ts`);
expect(output).not.toContain('UPDATE nx.json');
const jsonFailing = readJson(`tools/generators/${failing}/schema.json`);
jsonFailing.properties = {};
+1 -1
View File
@@ -295,7 +295,7 @@ describe('Nx Plugin', () => {
if (basename(f) === 'my-project-file') {
return {
build: {
executor: "@nrwl/workspace:run-commands",
executor: "nx:run-commands",
options: {
command: "echo 'custom registered target'"
}
+1 -1
View File
@@ -107,7 +107,7 @@ describe('cache', () => {
// updateFile('workspace.json', (c) => {
// const workspaceJson = JSON.parse(c);
// workspaceJson.projects[myapp1].targets.lint = {
// executor: '@nrwl/workspace:run-commands',
// executor: 'nx:run-commands',
// options: {
// command: 'echo hi && exit 1',
// },
+1 -1
View File
@@ -164,7 +164,7 @@ describe('Nx Running Tests', () => {
projectFilePatterns: ['inferred-project.nxproject'],
registerProjectTargets: () => ({
"echo": {
"executor": "@nrwl/workspace:run-commands",
"executor": "nx:run-commands",
"options": {
"command": "echo inferred-target"
}
+2
View File
@@ -27,6 +27,8 @@ describe('Webpack Plugin', () => {
runCLI(`build ${myPkg}`);
let output = runCommand(`node dist/libs/${myPkg}/main.js`);
expect(output).toMatch(/Hello/);
expect(output).not.toMatch(/Conflicting/);
expect(output).not.toMatch(/process.env.NODE_ENV/);
updateProjectConfig(myPkg, (config) => {
delete config.targets.build;
+1 -1
View File
@@ -449,7 +449,7 @@ export class GraphService {
this.renderGraph.$id(currentFocusedProjectName).addClass('focused');
}
this.renderGraph.on('zoom', () => {
this.renderGraph.on('zoom pan', () => {
this.tooltipService.hideAll();
});
+5
View File
@@ -4,6 +4,11 @@
@tailwind base;
@tailwind utilities;
/** Scrollbars **/
.dark {
color-scheme: dark;
}
$gray: rgb(100, 116, 139, 1);
#app,
+1 -1
View File
@@ -1,6 +1,6 @@
{
"packages": ["build/packages/*"],
"version": "14.8.0",
"version": "14.8.5",
"granularPathspec": false,
"command": {
"publish": {
+17 -18
View File
@@ -53,16 +53,16 @@
"@ngrx/effects": "~14.0.0",
"@ngrx/router-store": "~14.0.0",
"@ngrx/store": "~14.0.0",
"@nrwl/cypress": "14.7.16",
"@nrwl/devkit": "14.7.16",
"@nrwl/eslint-plugin-nx": "14.7.16",
"@nrwl/jest": "14.7.16",
"@nrwl/js": "14.7.16",
"@nrwl/linter": "14.7.16",
"@nrwl/next": "14.7.16",
"@nrwl/nx-cloud": "14.6.2",
"@nrwl/react": "14.7.16",
"@nrwl/web": "14.7.16",
"@nrwl/cypress": "14.8.1",
"@nrwl/devkit": "14.8.1",
"@nrwl/eslint-plugin-nx": "14.8.1",
"@nrwl/jest": "14.8.1",
"@nrwl/js": "14.8.1",
"@nrwl/linter": "14.8.1",
"@nrwl/next": "14.8.1",
"@nrwl/nx-cloud": "14.7.0",
"@nrwl/react": "14.8.1",
"@nrwl/web": "14.8.1",
"@parcel/watcher": "2.0.4",
"@phenomnomnominal/tsquery": "4.1.1",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.7",
@@ -110,10 +110,10 @@
"@types/tmp": "^0.2.0",
"@types/yargs": "^17.0.10",
"@types/yarnpkg__lockfile": "^1.1.5",
"@typescript-eslint/eslint-plugin": "^5.36.1",
"@typescript-eslint/parser": "^5.36.1",
"@typescript-eslint/eslint-plugin": "5.38.1",
"@typescript-eslint/parser": "5.38.1",
"@typescript-eslint/type-utils": "^5.36.1",
"@typescript-eslint/utils": "^5.36.1",
"@typescript-eslint/utils": "5.38.1",
"@xstate/immer": "^0.2.0",
"@xstate/inspect": "^0.5.1",
"@xstate/react": "^1.6.3",
@@ -174,7 +174,7 @@
"jsonc-eslint-parser": "^2.1.0",
"jsonc-parser": "3.2.0",
"kill-port": "^1.6.1",
"lerna": "5.0.0-alpha.2",
"lerna": "5.5.4",
"less": "3.12.2",
"less-loader": "^10.1.0",
"license-webpack-plugin": "^4.0.2",
@@ -183,13 +183,12 @@
"magic-string": "~0.26.2",
"memfs": "^3.0.1",
"metro-resolver": "^0.72.2",
"mime": "2.4.4",
"mini-css-extract-plugin": "~2.4.7",
"minimatch": "3.0.5",
"next-sitemap": "^3.1.10",
"ng-packagr": "~14.2.0",
"node-fetch": "^2.6.7",
"nx": "14.7.16",
"nx": "14.8.1",
"open": "^8.4.0",
"parse-markdown-links": "^1.0.4",
"parse5": "4.0.0",
@@ -252,7 +251,7 @@
"xstate": "^4.25.0",
"yargs": "^17.4.0",
"yargs-parser": "21.0.1",
"@nrwl/storybook": "14.7.16"
"@nrwl/storybook": "14.8.1"
},
"author": "Victor Savkin",
"license": "MIT",
@@ -277,7 +276,7 @@
"@yarnpkg/lockfile": "^1.1.0",
"@yarnpkg/parsers": "^3.0.0-rc.18",
"@zkochan/js-yaml": "0.0.6",
"axios": "0.21.1",
"axios": "1.0.0",
"classnames": "^2.3.1",
"cliui": "^7.0.2",
"core-js": "^3.6.5",
@@ -1,7 +1,11 @@
import { joinPathFragments, logger, Tree } from '@nrwl/devkit';
import {
formatFiles,
joinPathFragments,
logger,
readProjectConfiguration,
Tree,
} from '@nrwl/devkit';
import type { Schema } from './schema';
import { readProjectConfiguration, formatFiles } from '@nrwl/devkit';
import { getMFProjects } from '../../utils/get-mf-projects';
import {
checkOutputNameMatchesProjectName,
@@ -12,7 +16,7 @@ import {
writeNewWebpackConfig,
} from './lib';
export default async function convertToWithMF(tree: Tree, schema: Schema) {
export async function convertToWithMF(tree: Tree, schema: Schema) {
const projects = new Set(getMFProjects(tree, { legacy: true }));
if (!projects.has(schema.project)) {
@@ -56,3 +60,5 @@ export default async function convertToWithMF(tree: Tree, schema: Schema) {
await formatFiles(tree);
}
export default convertToWithMF;
+3 -1
View File
@@ -17,7 +17,7 @@ import { addStandaloneRoute } from '../../utils/nx-devkit/standalone-utils';
import { setupMf } from '../setup-mf/setup-mf';
import { E2eTestRunner } from '../../utils/test-runners';
export default async function host(tree: Tree, options: Schema) {
export async function host(tree: Tree, options: Schema) {
const projects = getProjects(tree);
const remotesToGenerate: string[] = [];
@@ -158,3 +158,5 @@ ${remoteRoutes}
}
);
}
export default host;
@@ -44,6 +44,7 @@ Object {
"affected": Object {
"defaultBase": "main",
},
"defaultProject": "app1",
"implicitDependencies": Object {
".eslintrc.json": "*",
"package.json": Object {
@@ -241,6 +241,11 @@ describe('workspace', () => {
).not.toBeDefined();
});
it('should set the default project correctly', async () => {
await migrateFromAngularCli(tree, {});
expect(readJson(tree, 'nx.json').defaultProject).toBe('myApp');
});
it('should create nx.json', async () => {
await migrateFromAngularCli(tree, { defaultBase: 'main' });
expect(readJson(tree, 'nx.json')).toMatchSnapshot();
@@ -3,8 +3,10 @@ import {
formatFiles,
installPackagesTask,
readJson,
readWorkspaceConfiguration,
Tree,
updateJson,
updateWorkspaceConfiguration,
} from '@nrwl/devkit';
import { nxVersion } from '../../utils/versions';
import type { GeneratorOptions } from './schema';
@@ -36,6 +38,8 @@ export async function migrateFromAngularCli(
const projects = getAllProjects(tree);
const options = normalizeOptions(tree, rawOptions, projects);
const defaultProject = projects.apps.find((app) => app.config.root === '');
if (options.preserveAngularCliLayout) {
addDependenciesToPackageJson(
tree,
@@ -103,6 +107,14 @@ export async function migrateFromAngularCli(
await formatFiles(tree);
}
if (defaultProject) {
const workspaceConfig = readWorkspaceConfiguration(tree);
updateWorkspaceConfiguration(tree, {
...workspaceConfig,
defaultProject: defaultProject.name,
});
}
if (!options.skipInstall) {
return () => {
installPackagesTask(tree);
@@ -28,7 +28,7 @@ function findNextAvailablePort(tree: Tree) {
return nextAvailablePort;
}
export default async function remote(tree: Tree, options: Schema) {
export async function remote(tree: Tree, options: Schema) {
const projects = getProjects(tree);
if (options.host && !projects.has(options.host)) {
throw new Error(
@@ -152,3 +152,5 @@ export class AppModule {}`
tree.write(pathToIndexHtml, newIndexContents);
}
}
export default remote;
@@ -4,7 +4,7 @@ import {
readCachedProjectGraph,
} from '@nrwl/devkit';
import { readCachedProjectConfiguration } from 'nx/src/project-graph/project-graph';
import { extname, join } from 'path';
import { extname } from 'path';
import {
getNpmPackageSharedConfig,
SharedLibraryConfig,
@@ -59,7 +59,11 @@ function mapRemotes(remotes: MFRemotes) {
const remoteLocationExt = extname(remoteLocation);
mappedRemotes[remoteName] = ['.js', '.mjs'].includes(remoteLocationExt)
? remoteLocation
: join(remoteLocation, 'remoteEntry.mjs');
: `${
remoteLocation.endsWith('/')
? remoteLocation.slice(0, -1)
: remoteLocation
}/remoteEntry.mjs`;
} else if (typeof remote === 'string') {
mappedRemotes[remote] = determineRemoteUrl(remote);
}
@@ -5,7 +5,7 @@ export function setupE2eProject(appName: string) {
const data = fs.readFileSync(`apps/${appName}-e2e/project.json`);
const json = JSON.parse(data.toString());
json.targets.e2e = {
executor: '@nrwl/workspace:run-commands',
executor: 'nx:run-commands',
options: {
commands: [`nx e2e-serve ${appName}-e2e`, `nx e2e-run ${appName}-e2e`],
},
@@ -19,7 +19,7 @@ export function setupE2eProject(appName: string) {
},
};
json.targets['e2e-serve'] = {
executor: '@nrwl/workspace:run-commands',
executor: 'nx:run-commands',
options: {
commands: [`nx serve ${appName}`],
readyWhen: 'can now view',
@@ -0,0 +1,83 @@
import axios from 'axios';
import { isCI } from './output';
export class PromptMessages {
private messages = {
nxCloudCreation: [
{
code: 'set-up-distributed-caching-ci',
message: `Enable distributed caching to make your CI faster`,
},
],
nxCloudMigration: [
{
code: 'we-noticed',
message: `We noticed you are migrating to a new major version, but are not taking advantage of Nx Cloud. Nx Cloud can make your CI up to 10 times faster. Learn more about it here: nx.app. Would you like to add it?`,
},
{
code: 'not-leveraging-caching',
message: `You're not leveraging distributed caching yet. Do you want to enable it and speed up your CI?`,
},
{
code: 'make-ci-faster',
message: `Enable distributed caching to make your CI faster?`,
},
],
};
private selectedMessages = {};
getPromptMessage(key: string): string {
if (this.selectedMessages[key] === undefined) {
if (process.env.NX_GENERATE_DOCS_PROCESS === 'true') {
this.selectedMessages[key] = 0;
} else {
this.selectedMessages[key] = Math.floor(
Math.random() * this.messages[key].length
);
}
}
return this.messages[key][this.selectedMessages[key]].message;
}
codeOfSelectedPromptMessage(key: string): string {
if (this.selectedMessages[key] === undefined) return null;
return this.messages[key][this.selectedMessages[key]].code;
}
}
export const messages = new PromptMessages();
/**
* We are incrementing a counter to track how often create-nx-workspace is used in CI
* vs dev environments. No personal information is collected.
*/
export async function recordStat(opts: {
command: string;
nxVersion: string;
useCloud: boolean;
meta: string;
}) {
try {
const major = Number(opts.nxVersion.split('.')[0]);
if (process.env.NX_VERBOSE_LOGGING === 'true') {
console.log(`Record stat. Major: ${major}`);
}
if (major < 10 || major > 14) return; // test version, skip it
await axios
.create({
baseURL: 'https://cloud.nx.app',
timeout: 400,
})
.post('/nx-cloud/stats', {
command: opts.command,
isCI: isCI(),
useCloud: opts.useCloud,
meta: opts.meta,
});
} catch (e) {
if (process.env.NX_VERBOSE_LOGGING === 'true') {
console.error(e);
}
}
}
@@ -5,7 +5,7 @@ import * as path from 'path';
import { dirSync } from 'tmp';
import * as yargs from 'yargs';
import { showNxWarning, unparse } from './shared';
import { isCI, output } from './output';
import { output } from './output';
import * as ora from 'ora';
import {
detectInvokedPackageManager,
@@ -23,7 +23,7 @@ import chalk = require('chalk');
import { ciList } from './ci';
import { join } from 'path';
import { initializeGitRepo } from './git';
import axios from 'axios';
import { messages, recordStat } from './ab-testing';
type Arguments = {
name: string;
@@ -62,37 +62,6 @@ enum Preset {
Express = 'express',
}
class PromptMessages {
private messages = {
nxCloud: [
{
code: 'set-up-distributed-caching-ci',
message: `Enable distributed caching to make your CI faster`,
},
],
};
private selectedMessages = {};
getPromptMessage(key: string): string {
if (this.selectedMessages[key] === undefined) {
if (process.env.NX_GENERATE_DOCS_PROCESS === 'true') {
this.selectedMessages[key] = 0;
} else {
this.selectedMessages[key] = Math.floor(
Math.random() * this.messages[key].length
);
}
}
return this.messages[key][this.selectedMessages[key]].message;
}
codeOfSelectedPromptMessage(key: string): string {
if (this.selectedMessages[key] === undefined) return null;
return this.messages[key][this.selectedMessages[key]].code;
}
}
const presetOptions: { name: Preset; message: string }[] = [
{
name: Preset.Apps,
@@ -162,8 +131,6 @@ const nxVersion = require('../package.json').version;
const tsVersion = 'TYPESCRIPT_VERSION'; // This gets replaced with the typescript version in the root package.json during build
const prettierVersion = 'PRETTIER_VERSION'; // This gets replaced with the prettier version in the root package.json during build
const messages = new PromptMessages();
export const commandsObject: yargs.Argv<Arguments> = yargs
.wrap(yargs.terminalWidth())
.parserConfiguration({
@@ -208,7 +175,7 @@ export const commandsObject: yargs.Argv<Arguments> = yargs
type: 'string',
})
.option('nxCloud', {
describe: chalk.dim(messages.getPromptMessage('nxCloud')),
describe: chalk.dim(messages.getPromptMessage('nxCloudCreation')),
type: 'boolean',
})
.option('ci', {
@@ -346,7 +313,12 @@ async function main(parsedArgs: yargs.Arguments<Arguments>) {
printNxCloudSuccessMessage(nxCloudInstallRes.stdout);
}
await recordWorkspaceCreationStats(nxCloud);
await recordStat({
nxVersion,
command: 'create-nx-workspace',
useCloud: nxCloud,
meta: messages.codeOfSelectedPromptMessage('nxCloudCreation'),
});
}
async function getConfiguration(
@@ -718,7 +690,7 @@ async function determineNxCloud(
.prompt([
{
name: 'NxCloud',
message: messages.getPromptMessage('nxCloud'),
message: messages.getPromptMessage('nxCloudCreation'),
type: 'autocomplete',
choices: [
{
@@ -1019,10 +991,7 @@ function pointToTutorialAndCourse(preset: Preset) {
output.addVerticalSeparator();
output.note({
title,
bodyLines: [
`https://nx.dev/react-tutorial/01-create-application`,
...pointToFreeCourseOnEgghead(),
],
bodyLines: [`https://nx.dev/react-tutorial/01-create-application`],
});
break;
case Preset.Angular:
@@ -1030,66 +999,15 @@ function pointToTutorialAndCourse(preset: Preset) {
output.addVerticalSeparator();
output.note({
title,
bodyLines: [
`https://nx.dev/angular-tutorial/01-create-application`,
...pointToFreeCourseOnYoutube(),
],
bodyLines: [`https://nx.dev/angular-tutorial/01-create-application`],
});
break;
case Preset.Nest:
output.addVerticalSeparator();
output.note({
title,
bodyLines: [
`https://nx.dev/node-tutorial/01-create-application`,
...pointToFreeCourseOnYoutube(),
],
bodyLines: [`https://nx.dev/node-tutorial/01-create-application`],
});
break;
}
}
function pointToFreeCourseOnYoutube(): string[] {
return [
``,
`Prefer watching videos? Check out this free Nx course on YouTube.`,
`https://www.youtube.com/watch?v=2mYLe9Kp9VM&list=PLakNactNC1dH38AfqmwabvOszDmKriGco`,
];
}
function pointToFreeCourseOnEgghead(): string[] {
return [
``,
`Prefer watching videos? Check out this free Nx course on Egghead.io.`,
`https://egghead.io/playlists/scale-react-development-with-nx-4038`,
];
}
/**
* We are incrementing a counter to track how often create-nx-workspace is used in CI
* vs dev environments. No personal information is collected.
*/
async function recordWorkspaceCreationStats(useCloud: boolean) {
try {
const major = Number(nxVersion.split('.')[0]);
if (process.env.NX_VERBOSE_LOGGING === 'true') {
console.log(`Record stat. Major: ${major}`);
}
if (major < 10 || major > 14) return; // test version, skip it
await axios
.create({
baseURL: 'https://cloud.nx.app',
timeout: 400,
})
.post('/nx-cloud/stats', {
command: 'create-nx-workspace',
isCI: isCI(),
useCloud,
meta: messages.codeOfSelectedPromptMessage('nxCloud'),
});
} catch (e) {
if (process.env.NX_VERBOSE_LOGGING === 'true') {
console.error(e);
}
}
}
+1 -1
View File
@@ -36,7 +36,7 @@
"tmp": "~0.2.1",
"tslib": "^2.3.0",
"yargs": "^17.4.0",
"axios": "0.21.1"
"axios": "^1.0.0"
},
"publishConfig": {
"access": "public"
@@ -2,7 +2,6 @@ import 'dotenv/config';
import * as chalk from 'chalk';
import type { ExecutorContext } from '@nrwl/devkit';
import { cacheDir, joinPathFragments, logger } from '@nrwl/devkit';
import { parse } from 'path';
import {
copyAssets,
copyPackageJson,
@@ -15,10 +14,9 @@ import { normalizeOptions } from './lib/normalize';
import { EsBuildExecutorOptions } from './schema';
import { removeSync, writeJsonSync } from 'fs-extra';
import { getClientEnvironment } from '../../utils/environment-variables';
import { createAsyncIterable } from '@nrwl/js/src/utils/create-async-iterable/create-async-iteratable';
import { buildEsbuildOptions } from './lib/build-esbuild-options';
const ESM_FILE_EXTENSION = '.js';
const CJS_FILE_EXTENSION = '.cjs';
const BUILD_WATCH_FAILED = `[ ${chalk.red(
@@ -40,70 +38,64 @@ export async function* esbuildExecutor(
const packageJsonResult = await copyPackageJson(
{
...options,
skipTypings: options.skipTypeCheck,
// TODO(jack): make types generate with esbuild
skipTypings: true,
outputFileExtensionForCjs: CJS_FILE_EXTENSION,
},
context
);
const esbuildOptions: esbuild.BuildOptions = {
...options.esbuildOptions,
entryPoints: [options.main, ...options.additionalEntryPoints],
entryNames:
options.outputHashing === 'all' ? '[dir]/[name].[hash]' : '[dir]/[name]',
outdir: options.outputPath,
bundle: true, // TODO(jack): support non-bundled builds
define: getClientEnvironment(),
external: options.external,
minify: options.minify,
platform: options.platform,
target: options.target,
metafile: options.metafile,
tsconfig: options.tsConfig,
};
if (options.watch) {
return yield* createAsyncIterable<{ success: boolean; outfile: string }>(
return yield* createAsyncIterable<{ success: boolean; outfile?: string }>(
async ({ next, done }) => {
let hasTypeErrors = false;
const results = await Promise.all(
options.format.map((format, idx) => {
const outfile = getOutfile(format, options, context);
return esbuild.build({
...esbuildOptions,
watch:
// Only emit info on one of the watch processes.
idx === 0
? {
onRebuild: async (
error: esbuild.BuildFailure,
result: esbuild.BuildResult
) => {
if (!options.skipTypeCheck) {
const { errors } = await runTypeCheck(
options,
context
);
hasTypeErrors = errors.length > 0;
}
const success = !error && !hasTypeErrors;
if (!success) {
logger.info(BUILD_WATCH_FAILED);
} else {
logger.info(BUILD_WATCH_SUCCEEDED);
}
next({ success: !!error && !hasTypeErrors, outfile });
},
}
: true,
options.format.map(async (format, idx) => {
const esbuildOptions = buildEsbuildOptions(
format,
outExtension: {
'.js': getOutExtension(format),
},
});
options,
context
);
const watch =
// Only emit info on one of the watch processes.
idx === 0
? {
onRebuild: async (
error: esbuild.BuildFailure,
result: esbuild.BuildResult
) => {
if (!options.skipTypeCheck) {
const { errors } = await runTypeCheck(options, context);
hasTypeErrors = errors.length > 0;
}
const success = !error && !hasTypeErrors;
if (!success) {
logger.info(BUILD_WATCH_FAILED);
} else {
logger.info(BUILD_WATCH_SUCCEEDED);
}
next({
success: !!error && !hasTypeErrors,
outfile: esbuildOptions.outfile,
});
},
}
: true;
try {
const result = await esbuild.build({ ...esbuildOptions, watch });
next({
success: true,
outfile: esbuildOptions.outfile,
});
return result;
} catch {
next({ success: false });
}
})
);
const processOnExit = () => {
@@ -133,23 +125,12 @@ export async function* esbuildExecutor(
} else {
logger.info(BUILD_WATCH_SUCCEEDED);
}
next({
success,
outfile: getOutfile(options.format[0], options, context),
});
}
);
} else {
const buildResults = await Promise.all(
options.format.map((format) =>
esbuild.build({
...esbuildOptions,
format,
outExtension: {
'.js': getOutExtension(format),
},
})
esbuild.build(buildEsbuildOptions(format, options, context))
)
);
const buildSuccess = buildResults.every((r) => r.errors?.length === 0);
@@ -200,24 +181,6 @@ function getTypeCheckOptions(
return typeCheckOptions;
}
function getOutExtension(format: 'cjs' | 'esm') {
return format === 'esm' ? ESM_FILE_EXTENSION : CJS_FILE_EXTENSION;
}
function getOutfile(
format: 'cjs' | 'esm',
options: EsBuildExecutorOptions,
context: ExecutorContext
) {
const candidate = joinPathFragments(
context.target.options.outputPath,
options.outputFileName
);
const ext = getOutExtension(format);
const { dir, name } = parse(candidate);
return `${dir}/${name}${ext}`;
}
async function runTypeCheck(
options: EsBuildExecutorOptions,
context: ExecutorContext
@@ -0,0 +1,154 @@
import { buildEsbuildOptions } from './build-esbuild-options';
import { ExecutorContext } from 'nx/src/config/misc-interfaces';
describe('buildEsbuildOptions', () => {
const context: ExecutorContext = {
workspace: {
version: 2,
projects: {
myapp: {
root: 'apps/myapp',
},
},
},
isVerbose: false,
root: '/',
cwd: '/',
target: {
executor: '@nrwl/esbuild:esbuild',
options: {
outputPath: 'dist/apps/myapp',
},
},
};
it('should include environment variables for platform === browser', () => {
expect(
buildEsbuildOptions(
'esm',
{
platform: 'browser',
main: 'apps/myapp/src/index.ts',
outputPath: 'dist/apps/myapp',
tsConfig: 'apps/myapp/tsconfig.app.json',
project: 'apps/myapp/package.json',
assets: [],
outputFileName: 'index.js',
singleEntry: true,
},
context
)
).toEqual({
bundle: true,
define: expect.objectContaining({
'process.env.NODE_ENV': '"test"',
}),
entryNames: '[dir]/[name]',
entryPoints: ['apps/myapp/src/index.ts'],
format: 'esm',
platform: 'browser',
outfile: 'dist/apps/myapp/index.js',
tsconfig: 'apps/myapp/tsconfig.app.json',
outExtension: {
'.js': '.js',
},
});
});
it('should support multiple entry points', () => {
expect(
buildEsbuildOptions(
'esm',
{
platform: 'browser',
main: 'apps/myapp/src/index.ts',
additionalEntryPoints: ['apps/myapp/src/extra-entry.ts'],
outputPath: 'dist/apps/myapp',
tsConfig: 'apps/myapp/tsconfig.app.json',
project: 'apps/myapp/package.json',
assets: [],
outputFileName: 'index.js',
singleEntry: false,
},
context
)
).toEqual({
bundle: true,
define: expect.objectContaining({
'process.env.NODE_ENV': '"test"',
}),
entryNames: '[dir]/[name]',
entryPoints: ['apps/myapp/src/index.ts', 'apps/myapp/src/extra-entry.ts'],
format: 'esm',
platform: 'browser',
outdir: 'dist/apps/myapp',
tsconfig: 'apps/myapp/tsconfig.app.json',
outExtension: {
'.js': '.js',
},
});
});
it('should support cjs format', () => {
expect(
buildEsbuildOptions(
'cjs',
{
platform: 'browser',
main: 'apps/myapp/src/index.ts',
outputPath: 'dist/apps/myapp',
tsConfig: 'apps/myapp/tsconfig.app.json',
project: 'apps/myapp/package.json',
assets: [],
outputFileName: 'index.js',
singleEntry: true,
},
context
)
).toEqual({
bundle: true,
define: expect.objectContaining({
'process.env.NODE_ENV': '"test"',
}),
entryNames: '[dir]/[name]',
entryPoints: ['apps/myapp/src/index.ts'],
format: 'cjs',
platform: 'browser',
outfile: 'dist/apps/myapp/index.cjs',
tsconfig: 'apps/myapp/tsconfig.app.json',
outExtension: {
'.js': '.cjs',
},
});
});
it('should not define environment variables for node', () => {
expect(
buildEsbuildOptions(
'cjs',
{
platform: 'node',
main: 'apps/myapp/src/index.ts',
outputPath: 'dist/apps/myapp',
tsConfig: 'apps/myapp/tsconfig.app.json',
project: 'apps/myapp/package.json',
assets: [],
outputFileName: 'index.js',
singleEntry: true,
},
context
)
).toEqual({
bundle: true,
entryNames: '[dir]/[name]',
entryPoints: ['apps/myapp/src/index.ts'],
format: 'cjs',
platform: 'node',
outfile: 'dist/apps/myapp/index.cjs',
tsconfig: 'apps/myapp/tsconfig.app.json',
outExtension: {
'.js': '.cjs',
},
});
});
});
@@ -0,0 +1,66 @@
import * as esbuild from 'esbuild';
import { getClientEnvironment } from '../../../utils/environment-variables';
import {
EsBuildExecutorOptions,
NormalizedEsBuildExecutorOptions,
} from '../schema';
import { ExecutorContext } from 'nx/src/config/misc-interfaces';
import { joinPathFragments } from 'nx/src/utils/path';
import { parse } from 'path';
const ESM_FILE_EXTENSION = '.js';
const CJS_FILE_EXTENSION = '.cjs';
export function buildEsbuildOptions(
format: 'cjs' | 'esm',
options: NormalizedEsBuildExecutorOptions,
context: ExecutorContext
): esbuild.BuildOptions {
const esbuildOptions: esbuild.BuildOptions = {
...options.esbuildOptions,
entryPoints: options.additionalEntryPoints
? [options.main, ...options.additionalEntryPoints]
: [options.main],
entryNames:
options.outputHashing === 'all' ? '[dir]/[name].[hash]' : '[dir]/[name]',
bundle: true, // TODO(jack): support non-bundled builds
external: options.external,
minify: options.minify,
platform: options.platform,
target: options.target,
metafile: options.metafile,
tsconfig: options.tsConfig,
format,
outExtension: { '.js': getOutExtension(format) },
};
if (options.platform === 'browser') {
esbuildOptions.define = getClientEnvironment();
}
if (options.singleEntry) {
esbuildOptions.outfile = getOutfile(format, options, context);
} else {
esbuildOptions.outdir = options.outputPath;
}
return esbuildOptions;
}
function getOutExtension(format: 'cjs' | 'esm') {
return format === 'esm' ? ESM_FILE_EXTENSION : CJS_FILE_EXTENSION;
}
function getOutfile(
format: 'cjs' | 'esm',
options: EsBuildExecutorOptions,
context: ExecutorContext
) {
const candidate = joinPathFragments(
context.target.options.outputPath,
options.outputFileName
);
const ext = getOutExtension(format);
const { dir, name } = parse(candidate);
return `${dir}/${name}${ext}`;
}
@@ -0,0 +1,79 @@
import { normalizeOptions } from './normalize';
describe('normalizeOptions', () => {
it('should handle single entry point options', () => {
expect(
normalizeOptions({
main: 'apps/myapp/src/index.ts',
outputPath: 'dist/apps/myapp',
tsConfig: 'apps/myapp/tsconfig.app.json',
project: 'apps/myapp/package.json',
assets: [],
})
).toEqual({
main: 'apps/myapp/src/index.ts',
outputPath: 'dist/apps/myapp',
tsConfig: 'apps/myapp/tsconfig.app.json',
project: 'apps/myapp/package.json',
assets: [],
outputFileName: 'index.js',
singleEntry: true,
});
});
it('should handle multiple entry point options', () => {
expect(
normalizeOptions({
main: 'apps/myapp/src/index.ts',
outputPath: 'dist/apps/myapp',
tsConfig: 'apps/myapp/tsconfig.app.json',
project: 'apps/myapp/package.json',
assets: [],
additionalEntryPoints: ['apps/myapp/src/extra-entry.ts'],
})
).toEqual({
main: 'apps/myapp/src/index.ts',
outputPath: 'dist/apps/myapp',
tsConfig: 'apps/myapp/tsconfig.app.json',
project: 'apps/myapp/package.json',
assets: [],
additionalEntryPoints: ['apps/myapp/src/extra-entry.ts'],
singleEntry: false,
});
});
it('should support custom output file name', () => {
expect(
normalizeOptions({
main: 'apps/myapp/src/index.ts',
outputPath: 'dist/apps/myapp',
tsConfig: 'apps/myapp/tsconfig.app.json',
project: 'apps/myapp/package.json',
assets: [],
outputFileName: 'test.js',
})
).toEqual({
main: 'apps/myapp/src/index.ts',
outputPath: 'dist/apps/myapp',
tsConfig: 'apps/myapp/tsconfig.app.json',
project: 'apps/myapp/package.json',
assets: [],
outputFileName: 'test.js',
singleEntry: true,
});
});
it('should validate against multiple entry points + outputFileName', () => {
expect(() =>
normalizeOptions({
main: 'apps/myapp/src/index.ts',
outputPath: 'dist/apps/myapp',
tsConfig: 'apps/myapp/tsconfig.app.json',
project: 'apps/myapp/package.json',
assets: [],
additionalEntryPoints: ['apps/myapp/src/extra-entry.ts'],
outputFileName: 'test.js',
})
).toThrow(/Cannot use/);
});
});
@@ -1,9 +1,29 @@
import { EsBuildExecutorOptions } from '../schema';
import { parse } from 'path';
import {
EsBuildExecutorOptions,
NormalizedEsBuildExecutorOptions,
} from '../schema';
export function normalizeOptions(
options: EsBuildExecutorOptions
): EsBuildExecutorOptions {
return {
...options,
outputFileName: options.outputFileName ?? 'main.js',
};
): NormalizedEsBuildExecutorOptions {
if (options.additionalEntryPoints?.length > 0) {
const { outputFileName, ...rest } = options;
if (outputFileName) {
throw new Error(
`Cannot use outputFileName and additionalEntry points together. Please remove outputFileName and try again.`
);
}
return {
...rest,
singleEntry: false,
};
} else {
return {
...options,
singleEntry: true,
outputFileName:
options.outputFileName ?? `${parse(options.main).name}.js`,
};
}
}
+5
View File
@@ -24,3 +24,8 @@ export interface EsBuildExecutorOptions {
updateBuildableProjectDepsInPackageJson?: boolean;
watch?: boolean;
}
export interface NormalizedEsBuildExecutorOptions
extends EsBuildExecutorOptions {
singleEntry: boolean;
}
@@ -151,6 +151,13 @@
"output": {
"type": "string",
"description": "Relative path within the output folder."
},
"ignore": {
"description": "An array of globs to ignore.",
"type": "array",
"items": {
"type": "string"
}
}
},
"additionalProperties": false,
+1 -10
View File
@@ -1,4 +1,5 @@
{
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "packages/expo/src",
"projectType": "library",
"targets": {
@@ -76,16 +77,6 @@
"options": {
"command": "node ./scripts/copy-readme.js expo"
}
},
"publish": {
"executor": "@nrwl/workspace:run-commands",
"options": {
"parallel": false,
"commands": [
"nx build expo",
"node tools/scripts/publish.mjs expo {args.ver} {args.tag}"
]
}
}
},
"tags": []
@@ -52,7 +52,7 @@ function getTargets(options: NormalizedSchema) {
};
architect.serve = {
executor: '@nrwl/workspace:run-commands',
executor: 'nx:run-commands',
options: {
command: `nx start ${options.name}`,
},
@@ -842,7 +842,7 @@ describe('lib', () => {
const config = readProjectConfiguration(tree, 'my-lib');
expect(config.targets.publish).toEqual({
executor: '@nrwl/workspace:run-commands',
executor: 'nx:run-commands',
options: {
command:
'node tools/scripts/publish.mjs my-lib {args.ver} {args.tag}',
@@ -132,7 +132,7 @@ function addProject(
const publishScriptPath = addMinimalPublishScript(tree);
projectConfiguration.targets.publish = {
executor: '@nrwl/workspace:run-commands',
executor: 'nx:run-commands',
options: {
command: `node ${publishScriptPath} ${options.name} {args.ver} {args.tag}`,
},
@@ -36,7 +36,9 @@ export const defaultFileEventHandler = (events: FileEvent[]) => {
dirs.forEach((d) => fse.ensureDirSync(d));
events.forEach((event) => {
if (event.type === 'create' || event.type === 'update') {
fse.copyFileSync(event.src, event.dest);
if (fse.lstatSync(event.src).isFile()) {
fse.copyFileSync(event.src, event.dest);
}
} else if (event.type === 'delete') {
fse.removeSync(event.dest);
} else {
@@ -145,4 +145,25 @@ describe('getUpdatedPackageJsonContent', () => {
},
});
});
it('should not set types when { skipTypings: true }', () => {
const json = getUpdatedPackageJsonContent(
{
name: 'test',
version: '0.0.1',
},
{
main: 'proj/src/index.ts',
outputPath: 'dist/proj',
projectRoot: 'proj',
skipTypings: true,
}
);
expect(json).toEqual({
name: 'test',
main: './src/index.js',
version: '0.0.1',
});
});
});
+9 -1
View File
@@ -6,7 +6,7 @@ import { daemonClient } from '../src/daemon/client/client';
(async () => {
try {
if (fileExists(join(workspaceRoot, 'nx.json'))) {
if (isMainNxPackage() && fileExists(join(workspaceRoot, 'nx.json'))) {
try {
await daemonClient.stop();
} catch (e) {}
@@ -27,3 +27,11 @@ import { daemonClient } from '../src/daemon/client/client';
}
}
})();
function isMainNxPackage() {
const mainNxPath = require.resolve('nx', {
paths: [workspaceRoot],
});
const thisNxPath = require.resolve('nx');
return mainNxPath === thisNxPath;
}
-1
View File
@@ -83,7 +83,6 @@ function shouldDelegateToAngularCLI() {
const commands = [
'add',
'analytics',
'deploy',
'config',
'doc',
'update',
+13 -9
View File
@@ -63,7 +63,8 @@
"v8-compile-cache": "2.3.0",
"yargs": "^17.4.0",
"yargs-parser": "21.0.1",
"js-yaml": "4.1.0"
"js-yaml": "4.1.0",
"axios": "^1.0.0"
},
"peerDependencies": {
"@swc-node/register": "^1.4.2",
@@ -80,31 +81,34 @@
"nx-migrations": {
"migrations": "./migrations.json",
"packageGroup": [
"@nrwl/workspace",
"@nrwl/angular",
"@nrwl/cli",
"@nrwl/cypress",
"@nrwl/detox",
"@nrwl/devkit",
"@nrwl/esbuild",
"@nrwl/eslint-plugin-nx",
"@nrwl/expo",
"@nrwl/express",
"@nrwl/jest",
"@nrwl/js",
"@nrwl/linter",
"@nrwl/nest",
"@nrwl/next",
"@nrwl/node",
"@nrwl/nx-plugin",
"@nrwl/react",
"@nrwl/react-native",
"@nrwl/rollup",
"@nrwl/storybook",
"@nrwl/web",
"@nrwl/js",
"@nrwl/cli",
"@nrwl/tao",
"@nrwl/web",
"@nrwl/webpack",
"@nrwl/workspace",
{
"package": "@nrwl/nx-cloud",
"version": "latest"
},
"@nrwl/react-native",
"@nrwl/detox",
"@nrwl/expo"
}
]
},
"executors": "./executors.json",
+1
View File
@@ -130,6 +130,7 @@ export async function affected(
break;
}
}
await output.drain();
} catch (e) {
printError(e, args.verbose);
process.exit(1);
@@ -29,7 +29,7 @@ export async function connectToNxCloudIfExplicitlyAsked(opts: {
export async function connectToNxCloudCommand(
promptOverride?: string
): Promise<void> {
): Promise<boolean> {
const nxJson = readNxJson();
const nxCloudUsed = Object.values(nxJson.tasksRunnerOptions).find(
(r) => r.runner == '@nrwl/nx-cloud'
@@ -38,16 +38,17 @@ export async function connectToNxCloudCommand(
output.log({
title: 'This workspace is already connected to Nx Cloud.',
});
return;
return false;
}
const res = await connectToNxCloudPrompt(promptOverride);
if (!res) return;
if (!res) return false;
const pmc = getPackageManagerCommand();
execSync(`${pmc.addDev} @nrwl/nx-cloud@latest`);
execSync(`${pmc.exec} nx g @nrwl/nx-cloud:init`, {
stdio: [0, 1, 2],
});
return true;
}
async function connectToNxCloudPrompt(prompt?: string) {
+10 -2
View File
@@ -34,6 +34,8 @@ import {
import { handleErrors } from '../utils/params';
import { connectToNxCloudCommand } from './connect-to-nx-cloud';
import { output } from '../utils/output';
import { messages, recordStat } from 'nx/src/utils/ab-testing';
import { nxVersion } from '../utils/versions';
export interface ResolvedMigrationConfiguration extends MigrationsJson {
packageGroup?: NxMigrationsConfiguration['packageGroup'];
@@ -815,9 +817,15 @@ async function generateMigrationsJsonAndUpdatePackageJson(
opts.targetVersion
))
) {
await connectToNxCloudCommand(
'We noticed you are migrating to a new major version, but are not taking advantage of Nx Cloud. Nx Cloud can make your CI up to 10 times faster. Learn more about it here: nx.app. Would you like to add it?'
const useCloud = await connectToNxCloudCommand(
messages.getPromptMessage('nxCloudMigration')
);
await recordStat({
command: 'migrate',
nxVersion,
useCloud,
meta: messages.codeOfSelectedPromptMessage('nxCloudMigration'),
});
originalPackageJson = readJsonFile<PackageJson>(
join(root, 'package.json')
);
+121 -23
View File
@@ -256,15 +256,10 @@ export const commandsObject = yargs
aliases: ['workspace-schematic [name]'],
builder: async (yargs) =>
linkToNxDevAndExamples(
await withWorkspaceGeneratorOptions(yargs),
await withWorkspaceGeneratorOptions(yargs, process.argv.slice(3)),
'workspace-generator'
),
handler: async () => {
await (
await import('./workspace-generators')
).workspaceGenerators(process.argv.slice(3));
process.exit(0);
},
handler: workspaceGeneratorHandler,
})
.command({
command: 'migrate [packageAndVersion]',
@@ -763,27 +758,130 @@ function withRunOneOptions(yargs: yargs.Argv) {
}
}
async function withWorkspaceGeneratorOptions(yargs: yargs.Argv) {
yargs
.option('list-generators', {
describe: 'List the available workspace-generators',
type: 'boolean',
})
.positional('name', {
type: 'string',
describe: 'The name of your generator',
});
type WorkspaceGeneratorProperties = {
[name: string]:
| {
type: yargs.Options['type'];
description?: string;
default?: any;
enum?: yargs.Options['type'][];
}
| {
type: yargs.PositionalOptionsType;
description?: string;
default?: any;
enum?: yargs.PositionalOptionsType[];
$default: {
$source: 'argv';
index: number;
};
};
};
/**
* Don't require `name` if only listing available
* schematics
*/
if ((await yargs.argv).listGenerators !== true) {
yargs.demandOption('name');
function isPositionalProperty(
property: WorkspaceGeneratorProperties[keyof WorkspaceGeneratorProperties]
): property is { type: yargs.PositionalOptionsType } {
return property['$default']?.['$source'] === 'argv';
}
async function withWorkspaceGeneratorOptions(
yargs: yargs.Argv,
args: string[]
) {
// filter out only positional arguments
args = args.filter((a) => !a.startsWith('-'));
if (args.length) {
// this is an actual workspace generator
return withCustomGeneratorOptions(yargs, args[0]);
} else {
yargs
.option('list-generators', {
describe: 'List the available workspace-generators',
type: 'boolean',
})
.positional('name', {
type: 'string',
describe: 'The name of your generator',
});
/**
* Don't require `name` if only listing available
* schematics
*/
if ((await yargs.argv).listGenerators !== true) {
yargs.demandOption('name');
}
return yargs;
}
}
async function withCustomGeneratorOptions(
yargs: yargs.Argv,
generatorName: string
) {
const schema = (
await import('./workspace-generators')
).workspaceGeneratorSchema(generatorName);
const options = [];
const positionals = [];
Object.entries(schema.properties as WorkspaceGeneratorProperties).forEach(
([name, prop]) => {
options.push({
name,
definition: {
describe: prop.description,
type: prop.type,
default: prop.default,
choices: prop.enum,
},
});
if (isPositionalProperty(prop)) {
positionals.push({
name,
definition: {
describe: prop.description,
type: prop.type,
choices: prop.enum,
},
});
}
}
);
let command = generatorName;
positionals.forEach(({ name }) => {
command += ` [${name}]`;
});
if (options.length) {
command += ' (options)';
}
yargs.command({
// this is the default and only command
command,
describe: schema.description || '',
builder: (y) => {
options.forEach(({ name, definition }) => {
y.option(name, definition);
});
positionals.forEach(({ name, definition }) => {
y.positional(name, definition);
});
return linkToNxDevAndExamples(y, 'workspace-generator');
},
handler: workspaceGeneratorHandler,
});
return yargs;
}
async function workspaceGeneratorHandler() {
await (
await import('./workspace-generators')
).workspaceGenerators(process.argv.slice(3));
process.exit(0);
}
function withMigrationOptions(yargs: yargs.Argv) {
const defaultCommitPrefix = 'chore: [nx migration] ';
+3
View File
@@ -20,6 +20,7 @@ export const packagesWeCareAbout = [
'@nrwl/cypress',
'@nrwl/detox',
'@nrwl/devkit',
'@nrwl/esbuild',
'@nrwl/eslint-plugin-nx',
'@nrwl/expo',
'@nrwl/express',
@@ -33,9 +34,11 @@ export const packagesWeCareAbout = [
'@nrwl/nx-plugin',
'@nrwl/react',
'@nrwl/react-native',
'@nrwl/rollup',
'@nrwl/schematics',
'@nrwl/storybook',
'@nrwl/web',
'@nrwl/webpack',
'@nrwl/workspace',
'typescript',
];
@@ -40,6 +40,17 @@ export async function workspaceGenerators(args: string[]) {
}
}
export function workspaceGeneratorSchema(name: string) {
const schemaFile = path.join(generatorsDir, name, 'schema.json');
if (fileExists(schemaFile)) {
return readJsonFile(schemaFile);
} else {
logger.error(`Cannot find schema for ${name}. Does the generator exist?`);
process.exit(1);
}
}
// compile tools
function compileTools() {
const toolsOutDir = getToolsOutDir();
+8 -7
View File
@@ -725,22 +725,23 @@ function buildProjectConfigurationFromPackageJson(
): ProjectConfiguration & { name: string } {
const directory = dirname(path).split('\\').join('/');
let name = packageJson.name ?? toProjectName(directory, nxJson);
if (nxJson.npmScope) {
if (nxJson?.npmScope) {
const npmPrefix = `@${nxJson.npmScope}/`;
if (name.startsWith(npmPrefix)) {
name = name.replace(npmPrefix, '');
}
}
const projectType =
nxJson?.workspaceLayout?.appsDir != nxJson?.workspaceLayout?.libsDir &&
nxJson?.workspaceLayout?.appsDir &&
directory.startsWith(nxJson.workspaceLayout.appsDir)
? 'application'
: 'library';
return {
root: directory,
sourceRoot: directory,
name,
projectType:
nxJson.workspaceLayout?.appsDir != nxJson.workspaceLayout?.libsDir &&
nxJson.workspaceLayout?.appsDir &&
directory.startsWith(nxJson.workspaceLayout.appsDir)
? 'application'
: 'library',
projectType,
};
}
+13 -5
View File
@@ -161,11 +161,19 @@ export class DaemonClient {
});
this.socket.on('close', () => {
output.error({
title: 'Daemon process terminated and closed the connection',
bodyLines: ['Please rerun the command, which will restart the daemon.'],
});
process.exit(1);
// it's ok for the daemon to terminate if the client doesn't wait on
// any messages from the daemon
if (this.queue.isEmpty()) {
this._connected = false;
} else {
output.error({
title: 'Daemon process terminated and closed the connection',
bodyLines: [
'Please rerun the command, which will restart the daemon.',
],
});
process.exit(1);
}
});
this.socket.on('error', (err) => {
+21 -6
View File
@@ -51,8 +51,13 @@ export type HandlerResult = {
response?: string;
};
let numberOfOpenConnections = 0;
const server = createServer(async (socket) => {
serverLogger.log('Established a connection');
numberOfOpenConnections += 1;
serverLogger.log(
`Established a connection. Number of open connections: ${numberOfOpenConnections}`
);
resetInactivityTimeout(handleInactivityTimeout);
if (!performanceObserver) {
performanceObserver = new PerformanceObserver((list) => {
@@ -75,7 +80,10 @@ const server = createServer(async (socket) => {
});
socket.on('close', () => {
serverLogger.log('Closed a connection');
numberOfOpenConnections -= 1;
serverLogger.log(
`Closed a connection. Number of open connections: ${numberOfOpenConnections}`
);
});
});
@@ -140,10 +148,17 @@ async function handleResult(socket: Socket, hr: HandlerResult) {
}
function handleInactivityTimeout() {
handleServerProcessTermination({
server,
reason: `${SERVER_INACTIVITY_TIMEOUT_MS}ms of inactivity`,
});
if (numberOfOpenConnections > 0) {
serverLogger.log(
`There are ${numberOfOpenConnections} open connections. Reset inactivity timer.`
);
resetInactivityTimeout(handleInactivityTimeout);
} else {
handleServerProcessTermination({
server,
reason: `${SERVER_INACTIVITY_TIMEOUT_MS}ms of inactivity`,
});
}
}
process
@@ -2,6 +2,7 @@ import Ajv from 'ajv';
import { Tree } from '../tree';
import { ProjectConfiguration } from '../../config/workspace-json-project-json';
import { createTree } from '../testing-utils/create-tree';
import {
createTreeWithEmptyWorkspace,
createTreeWithEmptyV1Workspace,
@@ -432,6 +433,23 @@ describe('project configuration', () => {
});
});
describe('getProjects', () => {
it('should get a map of projects', () => {
addProjectConfiguration(tree, 'proj', {
root: 'proj',
});
const projects = getProjects(tree);
expect(projects.size).toEqual(1);
expect(projects.get('proj')).toEqual({
$schema: '../node_modules/nx/schemas/project-schema.json',
name: 'proj',
root: 'proj',
});
});
});
describe('without nx.json', () => {
beforeEach(() => tree.delete('nx.json'));
@@ -568,6 +586,64 @@ describe('project configuration', () => {
)
).toBeFalsy();
});
describe('getProjects', () => {
it('should get a map of projects', () => {
addProjectConfiguration(tree, 'proj', {
root: 'proj',
});
const projects = getProjects(tree);
expect(projects.size).toEqual(1);
expect(projects.get('proj')).toEqual({
$schema: '../node_modules/nx/schemas/project-schema.json',
name: 'proj',
root: 'proj',
});
});
});
});
});
describe('for npm workspaces', () => {
beforeEach(() => {
tree = createTree();
});
describe('readProjectConfiguration', () => {
it('should read project configuration from package.json files', () => {
writeJson(tree, 'proj/package.json', {
name: 'proj',
});
const proj = readProjectConfiguration(tree, 'proj');
expect(proj).toEqual({
root: 'proj',
sourceRoot: 'proj',
projectType: 'library',
});
});
});
describe('getProjects', () => {
beforeEach(() => {
writeJson(tree, 'proj/package.json', {
name: 'proj',
});
});
it('should get a map of projects', () => {
const projects = getProjects(tree);
expect(projects.size).toEqual(1);
expect(projects.get('proj')).toEqual({
root: 'proj',
sourceRoot: 'proj',
projectType: 'library',
});
});
});
});
});
@@ -465,6 +465,7 @@ function findDeletedProjects(tree: Tree) {
}
let staticFSWorkspace: RawProjectsConfigurations;
let cachedTree: Tree;
function readRawWorkspaceJson(tree: Tree): RawProjectsConfigurations {
const path = getWorkspacePath(tree);
if (path && tree.exists(path)) {
@@ -477,13 +478,14 @@ function readRawWorkspaceJson(tree: Tree): RawProjectsConfigurations {
findCreatedProjects(tree),
(file) => readJson(tree, file)
).projects;
// We already have built a cache
if (!staticFSWorkspace) {
// We already have built a cache but need to confirm it's the same tree
if (!staticFSWorkspace || tree !== cachedTree) {
staticFSWorkspace = buildWorkspaceConfigurationFromGlobs(
nxJson,
[...globForProjectFiles(tree.root, nxJson)],
(file) => readJson(tree, file)
);
cachedTree = tree;
}
const projects = { ...staticFSWorkspace.projects, ...createdProjects };
findDeletedProjects(tree).forEach((file) => {
@@ -498,10 +500,11 @@ function readRawWorkspaceJson(tree: Tree): RawProjectsConfigurations {
delete projects[matchingStaticProject[0]];
}
});
return {
staticFSWorkspace = {
...staticFSWorkspace,
projects,
};
return staticFSWorkspace;
}
}
+8 -1
View File
@@ -3,6 +3,8 @@ import { workspaceRoot } from '../utils/workspace-root';
import { NodeBasedFileHasher } from './node-based-file-hasher';
import { FileHasherBase } from './file-hasher-base';
import { execSync } from 'child_process';
import { existsSync } from 'fs';
import { join } from 'path';
function createFileHasher(): FileHasherBase {
// special case for unit tests
@@ -11,7 +13,12 @@ function createFileHasher(): FileHasherBase {
}
try {
execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
return new GitBasedFileHasher();
// we don't use git based hasher when the repo uses git submodules
if (!existsSync(join(workspaceRoot, '.git', 'modules'))) {
return new GitBasedFileHasher();
} else {
return new NodeBasedFileHasher();
}
} catch {
return new NodeBasedFileHasher();
}
@@ -50,7 +50,7 @@ export class NodeBasedFileHasher extends FileHasherBase {
}
try {
const s = statSync(absoluteChild);
if (!s.isDirectory()) {
if (s.isFile()) {
this.fileHashes.set(
normalizePath(relChild),
this.hashFile(relChild)
+3 -2
View File
@@ -89,9 +89,9 @@ export function calculateFileChanges(
}
switch (ext) {
case '.json':
const atBase = readFileAtRevision(f, nxArgs.base);
const atHead = readFileAtRevision(f, nxArgs.head);
try {
const atBase = readFileAtRevision(f, nxArgs.base);
const atHead = readFileAtRevision(f, nxArgs.head);
return jsonDiff(JSON.parse(atBase), JSON.parse(atHead));
} catch (e) {
return [new WholeFileChange()];
@@ -122,6 +122,7 @@ function defaultReadFileAtRevision(
? readFileSync(file, 'utf-8')
: execSync(`git show ${revision}:${filePathInGitRepository}`, {
maxBuffer: TEN_MEGABYTES,
stdio: ['pipe', 'pipe', 'ignore'],
})
.toString()
.trim();
+5
View File
@@ -181,6 +181,11 @@ export class Cache {
}
private async copy(src: string, destination: string): Promise<void> {
// 'cp -a /path/dir/ dest/' operates differently to 'cp -a /path/dir dest/'
// --> which means actual build works but subsequent populate from cache (using cp -a) does not
// --> the fix is to remove trailing slashes to ensure consistent & expected behaviour
src = src.replace(/[\/\\]$/, '');
if (this.useFsExtraToCopyAndRemove) {
return copy(src, destination);
}
+83
View File
@@ -0,0 +1,83 @@
import axios from 'axios';
import { isCI } from './is-ci';
export class PromptMessages {
private messages = {
nxCloudCreation: [
{
code: 'set-up-distributed-caching-ci',
message: `Enable distributed caching to make your CI faster`,
},
],
nxCloudMigration: [
{
code: 'we-noticed',
message: `We noticed you are migrating to a new major version, but are not taking advantage of Nx Cloud. Nx Cloud can make your CI up to 10 times faster. Learn more about it here: nx.app. Would you like to add it?`,
},
{
code: 'not-leveraging-caching',
message: `You're not leveraging distributed caching yet. Do you want to enable it and speed up your CI?`,
},
{
code: 'make-ci-faster',
message: `Enable distributed caching to make your CI faster?`,
},
],
};
private selectedMessages = {};
getPromptMessage(key: string): string {
if (this.selectedMessages[key] === undefined) {
if (process.env.NX_GENERATE_DOCS_PROCESS === 'true') {
this.selectedMessages[key] = 0;
} else {
this.selectedMessages[key] = Math.floor(
Math.random() * this.messages[key].length
);
}
}
return this.messages[key][this.selectedMessages[key]].message;
}
codeOfSelectedPromptMessage(key: string): string {
if (this.selectedMessages[key] === undefined) return null;
return this.messages[key][this.selectedMessages[key]].code;
}
}
export const messages = new PromptMessages();
/**
* We are incrementing a counter to track how often create-nx-workspace is used in CI
* vs dev environments. No personal information is collected.
*/
export async function recordStat(opts: {
command: string;
nxVersion: string;
useCloud: boolean;
meta: string;
}) {
try {
const major = Number(opts.nxVersion.split('.')[0]);
if (process.env.NX_VERBOSE_LOGGING === 'true') {
console.log(`Record stat. Major: ${major}`);
}
if (major < 10 || major > 14) return; // test version, skip it
await axios
.create({
baseURL: 'https://cloud.nx.app',
timeout: 400,
})
.post('/nx-cloud/stats', {
command: opts.command,
isCI: isCI(),
useCloud: opts.useCloud,
meta: opts.meta,
});
} catch (e) {
if (process.env.NX_VERBOSE_LOGGING === 'true') {
console.error(e);
}
}
}
-62
View File
@@ -1,62 +0,0 @@
import { logger } from './logger';
describe('Logger', () => {
it('should color the NX prefix', () => {
let logObject;
jest.spyOn(console, 'info').mockImplementation((message) => {
logObject = message;
});
logger.info('NX some Nx message!');
if (process.env.CI === undefined) {
expect(logObject).toMatchInlineSnapshot(`
"
>  NX  some Nx message!
"
`);
} else {
expect(logObject).toMatchInlineSnapshot(`
"
> NX some Nx message!
"
`);
}
});
it('should log the full stack trace when an object is being passed', () => {
let logObject;
jest.spyOn(console, 'error').mockImplementation((message) => {
logObject = message;
});
const err = new Error(
'TypeError: Cannot read property target of undefined'
);
err.stack = `TypeError: Cannot read property 'target' of undefined
at /someuser/node_modules/@storybook/angular/dist/ts3.9/server/angular-devkit-build-webpack.js:145:49
at step (/someuser/node_modules/@storybook/angular/dist/ts3.9/server/angular-devkit-build-webpack.js:69:23)
at Object.next (/someuser/node_modules/@storybook/angular/dist/ts3.9/server/angular-devkit-build-webpack.js:50:53)
at fulfilled (/someuser/node_modules/@storybook/angular/dist/ts3.9/server/angular-devkit-build-webpack.js:41:58)`;
logger.error(err);
if (process.env.CI === undefined) {
expect(logObject).toMatchInlineSnapshot(`
"TypeError: Cannot read property 'target' of undefined
 at /someuser/node_modules/@storybook/angular/dist/ts3.9/server/angular-devkit-build-webpack.js:145:49
 at step (/someuser/node_modules/@storybook/angular/dist/ts3.9/server/angular-devkit-build-webpack.js:69:23)
 at Object.next (/someuser/node_modules/@storybook/angular/dist/ts3.9/server/angular-devkit-build-webpack.js:50:53)
 at fulfilled (/someuser/node_modules/@storybook/angular/dist/ts3.9/server/angular-devkit-build-webpack.js:41:58)"
`);
} else {
expect(logObject).toMatchInlineSnapshot(`
"TypeError: Cannot read property 'target' of undefined
at /someuser/node_modules/@storybook/angular/dist/ts3.9/server/angular-devkit-build-webpack.js:145:49
at step (/someuser/node_modules/@storybook/angular/dist/ts3.9/server/angular-devkit-build-webpack.js:69:23)
at Object.next (/someuser/node_modules/@storybook/angular/dist/ts3.9/server/angular-devkit-build-webpack.js:50:53)
at fulfilled (/someuser/node_modules/@storybook/angular/dist/ts3.9/server/angular-devkit-build-webpack.js:41:58)"
`);
}
});
});
+10
View File
@@ -267,6 +267,16 @@ class CLIOutput {
this.addNewline();
}
drain(): Promise<void> {
return new Promise((resolve) => {
if (process.stdout.writableNeedDrain) {
process.stdout.once('drain', resolve);
} else {
resolve();
}
});
}
}
export const output = new CLIOutput();
+35 -8
View File
@@ -2,31 +2,47 @@ import * as chalk from 'chalk';
import { output } from '../output';
import type { CorePlugin, PluginCapabilities } from './models';
export function fetchCorePlugins() {
const corePlugins: CorePlugin[] = [
export function fetchCorePlugins(): CorePlugin[] {
return [
{
name: '@nrwl/angular',
capabilities: 'generators',
capabilities: 'executors,generators',
},
{
name: '@nrwl/cypress',
capabilities: 'executors,generators',
},
{
name: '@nrwl/express',
name: '@nrwl/detox',
capabilities: 'executors,generators',
},
{
name: '@nrwl/esbuild',
capabilities: 'executors,generators',
},
{
name: '@nrwl/expo',
capabilities: 'executors,generators',
},
{
name: '@nrwl/express',
capabilities: 'generators',
},
{
name: '@nrwl/jest',
capabilities: 'executors,generators',
},
{
name: '@nrwl/js',
capabilities: 'executors,generators',
},
{
name: '@nrwl/linter',
capabilities: 'executors',
capabilities: 'executors,generators',
},
{
name: '@nrwl/nest',
capabilities: 'executors,generators',
capabilities: 'generators',
},
{
name: '@nrwl/next',
@@ -36,6 +52,10 @@ export function fetchCorePlugins() {
name: '@nrwl/node',
capabilities: 'executors,generators',
},
{
name: 'nx',
capabilities: 'executors',
},
{
name: '@nrwl/nx-plugin',
capabilities: 'executors,generators',
@@ -45,7 +65,11 @@ export function fetchCorePlugins() {
capabilities: 'executors,generators',
},
{
name: '@nrwl/js',
name: '@nrwl/react-native',
capabilities: 'executors,generators',
},
{
name: '@nrwl/rollup',
capabilities: 'executors,generators',
},
{
@@ -56,12 +80,15 @@ export function fetchCorePlugins() {
name: '@nrwl/web',
capabilities: 'executors,generators',
},
{
name: '@nrwl/webpack',
capabilities: 'executors,generators',
},
{
name: '@nrwl/workspace',
capabilities: 'executors,generators',
},
];
return corePlugins;
}
export function listCorePlugins(
@@ -165,7 +165,7 @@ describe('project graph utils', () => {
it('should prefer project.json targets', () => {
const projectJsonTargets = {
build: {
executor: '@nrwl/workspace:run-commands',
executor: 'nx:run-commands',
options: {
command: 'echo 2',
},
@@ -182,7 +182,7 @@ describe('project graph utils', () => {
it('should provide targets from project.json and package.json', () => {
const projectJsonTargets = {
clean: {
executor: '@nrwl/workspace:run-commands',
executor: 'nx:run-commands',
options: {
command: 'echo 2',
},
@@ -243,14 +243,14 @@ describe('project graph utils', () => {
const result = mergeNpmScriptsWithTargets('', {
build: {
executor: '@nrwl/workspace:run-commands',
executor: 'nx:run-commands',
options: { command: 'echo hi' },
},
});
expect(result).toEqual({
build: {
executor: '@nrwl/workspace:run-commands',
executor: 'nx:run-commands',
options: { command: 'echo hi' },
},
test: {
@@ -1,7 +1,9 @@
export class PromisedBasedQueue {
private counter = 0;
private promise = Promise.resolve(null);
sendToQueue(fn: () => Promise<any>): Promise<any> {
this.counter++;
let res, rej;
const r = new Promise((_res, _rej) => {
res = _res;
@@ -12,17 +14,25 @@ export class PromisedBasedQueue {
.then(async () => {
try {
res(await fn());
this.counter--;
} catch (e) {
rej(e);
this.counter--;
}
})
.catch(async () => {
try {
res(await fn());
this.counter--;
} catch (e) {
rej(e);
this.counter--;
}
});
return r;
}
isEmpty() {
return this.counter === 0;
}
}
@@ -41,7 +41,7 @@ function getTargets(options: NormalizedSchema) {
};
architect.serve = {
executor: '@nrwl/workspace:run-commands',
executor: 'nx:run-commands',
options: {
command: `nx start ${options.name}`,
},
+1
View File
@@ -49,6 +49,7 @@
"minimatch": "3.0.5",
"react-refresh": "^0.10.0",
"semver": "7.3.4",
"style-loader": "^3.3.0",
"stylus": "^0.55.0",
"stylus-loader": "^6.2.0",
"url-loader": "^4.1.1",
@@ -22,7 +22,7 @@ import {
SharedLibraryConfig,
} from './models';
import { readRootPackageJson } from './package-json';
import { extname, join } from 'path';
import { extname } from 'path';
import ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
function collectDependencies(
@@ -133,7 +133,11 @@ function mapRemotes(remotes: Remotes, projectGraph: ProjectGraph) {
const remoteLocationExt = extname(remoteLocation);
mappedRemotes[remoteName] = ['.js', '.mjs'].includes(remoteLocationExt)
? remoteLocation
: join(remoteLocation, 'remoteEntry.js');
: `${
remoteLocation.endsWith('/')
? remoteLocation.slice(0, -1)
: remoteLocation
}/remoteEntry.js`;
} else if (typeof remote === 'string') {
mappedRemotes[remote] = determineRemoteUrl(remote, projectGraph);
}
@@ -26,13 +26,12 @@ export async function rollupProjectGenerator(
}
function checkForTargetConflicts(tree: Tree, options: RollupProjectSchema) {
if (options.skipValidation) return;
const project = readProjectConfiguration(tree, options.project);
if (project.targets.build) {
throw new Error(`Project "${project.name}" already has a build target.`);
}
if (options.devServer && project.targets.serve) {
throw new Error(`Project "${project.name}" already has a serve target.`);
if (project.targets?.build) {
throw new Error(
`Project "${options.project}" already has a build target. Pass --skipValidation to ignore this error.`
);
}
}
+1 -1
View File
@@ -3,9 +3,9 @@ export interface RollupProjectSchema {
main?: string;
tsConfig?: string;
compiler?: 'babel' | 'swc' | 'tsc';
devServer?: boolean;
skipFormat?: boolean;
skipPackageJson?: boolean;
skipValidation?: boolean;
importPath?: string;
external?: string[];
rollupConfig?: string;
@@ -41,6 +41,11 @@
"default": false,
"description": "Do not add dependencies to `package.json`."
},
"skipValidation": {
"type": "boolean",
"default": false,
"description": "Do not perform any validation on existing project."
},
"importPath": {
"type": "string",
"description": "The library name used to import it, like `@myorg/my-awesome-lib`."
+52 -60
View File
@@ -1,15 +1,11 @@
import * as ts from 'typescript';
import { stripIndents } from '@nrwl/devkit';
import { findBuilderInMainJsTs } from './utils';
import { logger } from '@nrwl/devkit';
import { builderIsWebpackButNotWebpack5 } from './utils';
describe('testing utilities', () => {
beforeEach(() => {
jest.spyOn(logger, 'warn');
});
it('should not log the webpack5 warning if builder is webpack5', () => {
const sourceCode = stripIndents`
describe('builderIsWebpackButNotWebpack5', () => {
it('should return false if builder is webpack5', () => {
const sourceCode = stripIndents`
const rootMain = require('../../../.storybook/main');
module.exports = {
@@ -18,19 +14,18 @@ describe('testing utilities', () => {
};
`;
const source = ts.createSourceFile(
'.storybook/main.js',
sourceCode,
ts.ScriptTarget.Latest,
true
);
const source = ts.createSourceFile(
'.storybook/main.js',
sourceCode,
ts.ScriptTarget.Latest,
true
);
findBuilderInMainJsTs(source);
expect(logger.warn).not.toHaveBeenCalled();
});
expect(builderIsWebpackButNotWebpack5(source)).toBeFalsy();
});
it('should not log the webpack5 warning if builder is @storybook/webpack5', () => {
const sourceCode = stripIndents`
it('should return false if builder is @storybook/webpack5', () => {
const sourceCode = stripIndents`
const rootMain = require('../../../.storybook/main');
module.exports = {
@@ -39,19 +34,18 @@ describe('testing utilities', () => {
};
`;
const source = ts.createSourceFile(
'.storybook/main.js',
sourceCode,
ts.ScriptTarget.Latest,
true
);
const source = ts.createSourceFile(
'.storybook/main.js',
sourceCode,
ts.ScriptTarget.Latest,
true
);
findBuilderInMainJsTs(source);
expect(logger.warn).not.toHaveBeenCalled();
});
expect(builderIsWebpackButNotWebpack5(source)).toBeFalsy();
});
it('should not log the webpack5 warning if builder exists but does not contain webpack', () => {
const sourceCode = stripIndents`
it('should return false if builder exists but does not contain webpack', () => {
const sourceCode = stripIndents`
const rootMain = require('../../../.storybook/main');
module.exports = {
@@ -60,19 +54,18 @@ describe('testing utilities', () => {
};
`;
const source = ts.createSourceFile(
'.storybook/main.js',
sourceCode,
ts.ScriptTarget.Latest,
true
);
const source = ts.createSourceFile(
'.storybook/main.js',
sourceCode,
ts.ScriptTarget.Latest,
true
);
findBuilderInMainJsTs(source);
expect(logger.warn).not.toHaveBeenCalled();
});
expect(builderIsWebpackButNotWebpack5(source)).toBeFalsy();
});
it('should log the webpack5 warning if builder is webpack4', () => {
const sourceCode = stripIndents`
it('should return true if builder is webpack4', () => {
const sourceCode = stripIndents`
const rootMain = require('../../../.storybook/main');
module.exports = {
@@ -81,19 +74,18 @@ describe('testing utilities', () => {
};
`;
const source = ts.createSourceFile(
'.storybook/main.js',
sourceCode,
ts.ScriptTarget.Latest,
true
);
const source = ts.createSourceFile(
'.storybook/main.js',
sourceCode,
ts.ScriptTarget.Latest,
true
);
findBuilderInMainJsTs(source);
expect(logger.warn).toHaveBeenCalled();
});
expect(builderIsWebpackButNotWebpack5(source)).toBeTruthy();
});
it('should log the webpack5 warning if builder does not exist', () => {
const sourceCode = stripIndents`
it('should return true if builder does not exist because default is webpack', () => {
const sourceCode = stripIndents`
const rootMain = require('../../../.storybook/main');
module.exports = {
@@ -101,14 +93,14 @@ describe('testing utilities', () => {
};
`;
const source = ts.createSourceFile(
'.storybook/main.js',
sourceCode,
ts.ScriptTarget.Latest,
true
);
const source = ts.createSourceFile(
'.storybook/main.js',
sourceCode,
ts.ScriptTarget.Latest,
true
);
findBuilderInMainJsTs(source);
expect(logger.warn).toHaveBeenCalled();
expect(builderIsWebpackButNotWebpack5(source)).toBeFalsy();
});
});
});
+51 -49
View File
@@ -47,39 +47,49 @@ export function runStorybookSetupCheck(options: CommonNxStorybookConfig) {
function reactWebpack5Check(options: CommonNxStorybookConfig) {
if (options.uiFramework === '@storybook/react') {
let storybookConfigFilePath = joinPathFragments(
options.config.configFolder,
'main.js'
);
if (!existsSync(storybookConfigFilePath)) {
storybookConfigFilePath = joinPathFragments(
options.config.configFolder,
'main.ts'
);
}
if (!existsSync(storybookConfigFilePath)) {
// looks like there's no main config file, so skip
return;
}
const source = mainJsTsFileContent(options.config.configFolder);
const rootSource = mainJsTsFileContent('.storybook');
// check whether the current Storybook configuration has the webpack 5 builder enabled
const storybookConfig = readFileSync(storybookConfigFilePath, {
encoding: 'utf8',
});
const source = ts.createSourceFile(
storybookConfigFilePath,
storybookConfig,
ts.ScriptTarget.Latest,
true
);
findBuilderInMainJsTs(source);
if (
builderIsWebpackButNotWebpack5(source) &&
builderIsWebpackButNotWebpack5(rootSource)
) {
logger.warn(`
It looks like you use Webpack 5 but your Storybook setup is not configured to leverage that
and thus falls back to Webpack 4.
Make sure you upgrade your Storybook config to use Webpack 5.
- https://gist.github.com/shilman/8856ea1786dcd247139b47b270912324#upgrade
`);
}
}
}
function mainJsTsFileContent(configFolder: string): ts.SourceFile {
let storybookConfigFilePath = joinPathFragments(configFolder, 'main.js');
if (!existsSync(storybookConfigFilePath)) {
storybookConfigFilePath = joinPathFragments(configFolder, 'main.ts');
}
if (!existsSync(storybookConfigFilePath)) {
// looks like there's no main config file, so skip
return;
}
const storybookConfig = readFileSync(storybookConfigFilePath, {
encoding: 'utf8',
});
return ts.createSourceFile(
storybookConfigFilePath,
storybookConfig,
ts.ScriptTarget.Latest,
true
);
}
function webpackFinalPropertyCheck(options: CommonNxStorybookConfig) {
let placesToCheck = [
{
@@ -135,33 +145,25 @@ export function resolveCommonStorybookOptionMapper(
return storybookOptions;
}
export function findBuilderInMainJsTs(storybookConfig: ts.SourceFile) {
export function builderIsWebpackButNotWebpack5(
storybookConfig: ts.SourceFile
): boolean {
const importArray = findNodes(storybookConfig, [
ts.SyntaxKind.PropertyAssignment,
]);
let builderIsSpecified = false;
let builderIsWebpackNot5 = false;
importArray.forEach((parent) => {
const identifier = findNodes(parent, ts.SyntaxKind.Identifier);
const sbBuilder = findNodes(parent, ts.SyntaxKind.StringLiteral);
const builderText = sbBuilder?.[0]?.getText() ?? '';
if (identifier[0].getText() === 'builder') {
builderIsSpecified = true;
if (
builderText.includes('webpack') &&
!builderText.includes('webpack5')
) {
builderIsSpecified = false;
}
if (
identifier?.[0]?.getText() === 'builder' &&
builderText.includes('webpack') &&
!builderText.includes('webpack5')
) {
builderIsWebpackNot5 = true;
}
});
if (!builderIsSpecified) {
logger.warn(`
It looks like you use Webpack 5 but your Storybook setup is not configured to leverage that
and thus falls back to Webpack 4.
Make sure you upgrade your Storybook config to use Webpack 5.
- https://gist.github.com/shilman/8856ea1786dcd247139b47b270912324#upgrade
`);
}
return builderIsWebpackNot5;
}
@@ -1,84 +1 @@
import { watch } from 'chokidar';
import { execSync } from 'child_process';
import { workspaceLayout } from '@nrwl/devkit';
import { joinPathFragments } from '@nrwl/devkit';
import ignore from 'ignore';
import { readFileSync } from 'fs';
export class WebpackNxBuildCoordinationPlugin {
private currentlyRunning: 'none' | 'nx-build' | 'webpack-build' = 'none';
constructor(private readonly buildCmd: string) {
this.buildChangedProjects();
this.startWatchingBuildableLibs();
}
apply(compiler) {
compiler.hooks.beforeCompile.tapPromise(
'IncrementalDevServerPlugin',
async () => {
while (this.currentlyRunning === 'nx-build') {
await sleep(50);
}
this.currentlyRunning = 'webpack-build';
}
);
compiler.hooks.done.tapPromise('IncrementalDevServerPlugin', async () => {
this.currentlyRunning = 'none';
});
}
startWatchingBuildableLibs() {
createFileWatcher(process.cwd(), () => {
this.buildChangedProjects();
});
}
async buildChangedProjects() {
while (this.currentlyRunning === 'webpack-build') {
await sleep(50);
}
this.currentlyRunning = 'nx-build';
try {
execSync(this.buildCmd, { stdio: [0, 1, 2] });
// eslint-disable-next-line no-empty
} catch (e) {}
this.currentlyRunning = 'none';
}
}
function sleep(time: number) {
return new Promise((resolve) => setTimeout(resolve, time));
}
function getIgnoredGlobs(root: string) {
const ig = ignore();
try {
ig.add(readFileSync(`${root}/.gitignore`, 'utf-8'));
} catch {}
try {
ig.add(readFileSync(`${root}/.nxignore`, 'utf-8'));
} catch {}
return ig;
}
function createFileWatcher(root: string, changeHandler: () => void) {
const ignoredGlobs = getIgnoredGlobs(root);
const layout = workspaceLayout();
const watcher = watch(
[
joinPathFragments(layout.appsDir, '**'),
joinPathFragments(layout.libsDir, '**'),
],
{
cwd: root,
ignoreInitial: true,
}
);
watcher.on('all', (_event: string, path: string) => {
if (ignoredGlobs.ignores(path)) return;
changeHandler();
});
return { close: () => watcher.close() };
}
export { WebpackNxBuildCoordinationPlugin } from '@nrwl/webpack/src/plugins/webpack-nx-build-coordination-plugin';
@@ -6,6 +6,7 @@ export interface WebpackProjectGeneratorSchema {
devServer?: boolean;
skipFormat?: boolean;
skipPackageJson?: boolean;
skipValidation?: boolean;
target?: 'node' | 'web';
webpackConfig?: string;
}
@@ -46,6 +46,11 @@
"default": false,
"description": "Do not add dependencies to `package.json`."
},
"skipValidation": {
"type": "boolean",
"default": false,
"description": "Do not perform any validation on existing project."
},
"devServer": {
"type": "boolean",
"description": "Add a serve target to run a local webpack dev-server",
@@ -29,13 +29,20 @@ function checkForTargetConflicts(
tree: Tree,
options: WebpackProjectGeneratorSchema
) {
if (options.skipValidation) return;
const project = readProjectConfiguration(tree, options.project);
if (project.targets.build) {
throw new Error(`Project "${project.name}" already has a build target.`);
if (project.targets?.build) {
throw new Error(
`Project "${project.name}" already has a build target. Pass --skipValidation to ignore this error.`
);
}
if (options.devServer && project.targets.serve) {
throw new Error(`Project "${project.name}" already has a serve target.`);
if (options.devServer && project.targets?.serve) {
throw new Error(
`Project "${project.name}" already has a serve target. Pass --skipValidation to ignore this error.`
);
}
}
@@ -4,6 +4,7 @@ import { workspaceLayout } from '@nrwl/devkit';
import { joinPathFragments } from '@nrwl/devkit';
import ignore from 'ignore';
import { readFileSync } from 'fs';
import type { Compiler } from 'webpack';
export class WebpackNxBuildCoordinationPlugin {
private currentlyRunning: 'none' | 'nx-build' | 'webpack-build' = 'none';
@@ -13,7 +14,7 @@ export class WebpackNxBuildCoordinationPlugin {
this.startWatchingBuildableLibs();
}
apply(compiler) {
apply(compiler: Compiler) {
compiler.hooks.beforeCompile.tapPromise(
'IncrementalDevServerPlugin',
async () => {
+9 -2
View File
@@ -37,6 +37,9 @@ export function getBaseWebpackPartial(
internalOptions: InternalBuildOptions,
context?: ExecutorContext
): Configuration {
// If the function is called directly and not through `@nrwl/webpack:webpack` then this target may not be set.
options.target ??= 'web';
const mainFields = [
...(internalOptions.esm ? ['es2015'] : []),
'module',
@@ -68,7 +71,7 @@ export function getBaseWebpackPartial(
) ?? {};
const webpackConfig: Configuration = {
target: options.target ?? 'web', // webpack defaults to 'browserslist' which breaks Fast Refresh
target: options.target,
entry: {
[mainEntry]: [options.main],
...additionalEntryPoints,
@@ -189,6 +192,8 @@ export function getBaseWebpackPartial(
runtimeChunk: true,
};
}
webpackConfig.optimization ??= {};
webpackConfig.optimization.nodeEnv = process.env.NODE_ENV ?? mode;
}
const extraPlugins: WebpackPluginInstance[] = [];
@@ -390,7 +395,7 @@ export function createLoaderFromCompiler(
}),
},
};
default:
case 'babel':
return {
test: /\.([jt])sx?$/,
loader: join(__dirname, 'web-babel-loader'),
@@ -408,5 +413,7 @@ export function createLoaderFromCompiler(
cacheCompression: false,
},
};
default:
return null;
}
}
@@ -193,8 +193,6 @@ export function getCommonConfig(
} catch {}
return {
mode:
scriptsOptimization || stylesOptimization ? 'production' : 'development',
profile: buildOptions.statsJson,
resolve: {
extensions: ['.ts', '.tsx', '.mjs', '.js'],
+11 -8
View File
@@ -34,26 +34,29 @@
"migrations": "./migrations.json",
"packageGroup": {
"@nrwl/angular": "*",
"@nrwl/cli": "*",
"@nrwl/cypress": "*",
"@nrwl/detox": "*",
"@nrwl/devkit": "*",
"@nrwl/esbuild": "*",
"@nrwl/eslint-plugin-nx": "*",
"@nrwl/expo": "*",
"@nrwl/express": "*",
"@nrwl/jest": "*",
"@nrwl/js": "*",
"@nrwl/linter": "*",
"@nrwl/nest": "*",
"@nrwl/next": "*",
"@nrwl/node": "*",
"@nrwl/nx-cloud": "latest",
"@nrwl/nx-plugin": "*",
"@nrwl/react": "*",
"@nrwl/storybook": "*",
"@nrwl/web": "*",
"@nrwl/js": "*",
"@nrwl/cli": "*",
"@nrwl/tao": "*",
"@nrwl/nx-cloud": "latest",
"@nrwl/react-native": "*",
"@nrwl/expo": "*",
"@nrwl/detox": "*",
"@nrwl/rollup": "*",
"@nrwl/storybook": "*",
"@nrwl/tao": "*",
"@nrwl/web": "*",
"@nrwl/webpack": "*",
"nx": "*"
}
},
@@ -103,7 +103,7 @@ describe('missingDependencies', () => {
example: [
{
source: 'example',
target: 'npm:formik',
target: 'missing',
type: DependencyType.static,
},
],
@@ -128,6 +128,14 @@ function collectDependencies(
): { name: string; isTopLevel: boolean }[] {
(projGraph.dependencies[project] || []).forEach((dependency) => {
if (!acc.some((dep) => dep.name === dependency.target)) {
// Temporary skip this. Currently the set of external nodes is built from package.json, not lock file.
// As a result, some nodes might be missing. This should not cause any issues, we can just skip them.
if (
dependency.target.startsWith('npm:') &&
!projGraph.externalNodes[dependency.target]
)
return;
acc.push({ name: dependency.target, isTopLevel: areTopLevelDeps });
if (!shallow) {
collectDependencies(dependency.target, projGraph, acc, shallow, false);
+920 -1828
View File
File diff suppressed because it is too large Load Diff