Compare commits

...

20 Commits

Author SHA1 Message Date
FrozenPandaz 7b7f1e7f72 chore(misc): publish 16.5.1 2023-07-10 13:55:11 -04:00
Chi Ma f525ba37ee fix(core): add missing perf_hooks import in tasks-runner/cache (#18043)
(cherry picked from commit e25582780a)
2023-07-10 11:12:22 -04:00
Leosvel Pérez Espinosa 5e8f9f4b1e fix(core): handle undefined target defaults (#18044)
(cherry picked from commit 92926b9346)
2023-07-10 11:12:10 -04:00
Leosvel Pérez Espinosa 13aa3be1f0 fix(angular): explicitly depend on find-cache-dir to avoid npm resolution conflict (#18049)
(cherry picked from commit a08cb91891)
2023-07-10 11:12:06 -04:00
Leosvel Pérez Espinosa f9a615bb04 fix(js): post-process skipped tasks by the ts compiler in tsc batch implementation (#17938)
(cherry picked from commit d10aeb49ac)
2023-07-10 11:12:00 -04:00
Katerina Skroumpelou 1a0e65a3d3 fix(storybook): dont throw if no project-level tsconfig (#18027)
(cherry picked from commit 878955b50d)
2023-07-10 11:11:47 -04:00
Nicholas Cunningham 25263f72d8 fix(nextjs): Update @types/react version for yarn resolution (#18028)
(cherry picked from commit b1f19e3e91)
2023-07-10 11:11:34 -04:00
Emily Xiong 9e448fe341 fix(js): restore registry for only localhost (#17999)
(cherry picked from commit 8d5cfcc2b3)
2023-07-07 16:02:12 -04:00
Miroslav Jonaš 802243e410 fix(core): ensure yarn runs install with correct version (#17997)
(cherry picked from commit d0c37727c5)
2023-07-07 16:02:04 -04:00
Katerina Skroumpelou 0c480fd94d fix(storybook): properly identify file as story (#18016)
(cherry picked from commit 21007d8922)
2023-07-07 16:02:00 -04:00
Katerina Skroumpelou b1a8917b8d fix(storybook): optional chain .length in utils (#18001)
(cherry picked from commit 3edbe49fa2)
2023-07-07 16:01:54 -04:00
Jack Hsu 0e15beafb2 feat(vite): pin Vite version range to patch releases only to avoid conflicts with Angular (#18010)
(cherry picked from commit 3fdc66dd69)
2023-07-07 16:01:46 -04:00
Miroslav Jonaš 66090ca3c9 chore(repo): bump versions of ci images (#17969)
(cherry picked from commit 601c65241b)
2023-07-07 16:01:33 -04:00
Nathan Walker 97ce47aa69 fix(js): remove duplicate alias (#17978)
(cherry picked from commit ccd195d60a)
2023-07-07 16:01:24 -04:00
Jack Hsu b39cafaf86 fix(js): add watchIgnore and runBuildTargetDependencies options to speed up build (#17953)
(cherry picked from commit c6a0615fa5)
2023-07-07 16:01:12 -04:00
Colum Ferry ea28f16faf fix(bundling): update the browsers for es5 terser check (#17784)
(cherry picked from commit 8c9ad0da69)
2023-07-07 16:01:05 -04:00
Colum Ferry 0d04949ffd fix(angular): automatically skip remotes not in the current workspace #17473 (#17497)
(cherry picked from commit c64f26ef3c)
2023-07-07 16:01:00 -04:00
Miroslav Jonaš ed22afecef fix(js): ignore dependencies from non-index root files for create-package-json (#17930)
(cherry picked from commit 28a071f243)
2023-07-07 16:00:55 -04:00
Vergil Penkov a496ef9144 fix(core): add license information for native packages (#17964)
(cherry picked from commit d42d2f46fb)
2023-07-07 16:00:52 -04:00
Denis Frenademetz 699af690b7 fix(core): ensure external dependency hashes are resolved in a deterministic way (#17926)
(cherry picked from commit 65adb94bf6)
2023-07-07 16:00:50 -04:00
61 changed files with 812 additions and 210 deletions
+18 -10
View File
@@ -5,7 +5,6 @@ version: 2.1
# -------------------------
orbs:
nx: nrwl/nx@1.6.1
node: circleci/node@5.1.0
rust: circleci/rust@1.6.0
browser-tools: circleci/browser-tools@1.4.0
@@ -20,14 +19,14 @@ executors:
linux:
<<: *defaults
docker:
- image: cimg/rust:1.66.1-browsers
- image: cimg/rust:1.70.0-browsers
resource_class: medium+
macos:
<<: *defaults
resource_class: macos.x86.medium.gen2
macos:
xcode: &_XCODE_VERSION '13.0.0'
xcode: '14.2.0'
# -------------------------
# COMMANDS
@@ -42,10 +41,22 @@ commands:
name: Restore pnpm Package Cache
keys:
- node-deps-{{ arch }}-v3-{{ checksum "pnpm-lock.yaml" }}
- run:
name: Install pnpm package manager
command: |
npm install -g @pnpm/exe@8.3.1
- when:
condition:
equal: [<< parameters.os >>, linux]
steps:
- run:
name: Install pnpm package manager (linux)
command: |
npm install --prefix=$HOME/.local -g @pnpm/exe@8.3.1
- when:
condition:
equal: [<< parameters.os >>, macos]
steps:
- run:
name: Install pnpm package manager (macos)
command: |
npm install -g @pnpm/exe@8.3.1
- run:
name: Install Dependencies
command: |
@@ -95,9 +106,6 @@ commands:
sudo apt-get install -y ca-certificates lsof
- browser-tools/install-chrome
- browser-tools/install-chromedriver
- node/install:
# Use LTS version
node-version: ''
- run-pnpm-install:
os: << parameters.os >>
@@ -107,7 +107,7 @@
"skipRemotes": {
"type": "array",
"items": { "type": "string" },
"description": "List of remote applications to not automatically serve, either statically or in development mode. This can be useful for multi-repository module federation setups where the host application uses a remote application from an external repository."
"description": "List of remote applications to not automatically serve, either statically or in development mode."
},
"pathToManifestFile": {
"type": "string",
@@ -72,7 +72,7 @@
"skipRemotes": {
"type": "array",
"items": { "type": "string" },
"description": "List of remote applications to not automatically serve, either statically or in development mode. This can be useful for multi-repository module federation setups where the host application uses a remote application from an external repository."
"description": "List of remote applications to not automatically serve, either statically or in development mode."
},
"verbose": {
"type": "boolean",
+20 -7
View File
@@ -28,12 +28,14 @@
"host": {
"type": "string",
"default": "localhost",
"description": "The host to inspect the process on."
"description": "The host to inspect the process on.",
"x-priority": "important"
},
"port": {
"type": "number",
"default": 9229,
"description": "The port to inspect the process on. Setting port to 0 will assign random free ports to all forked processes."
"description": "The port to inspect the process on. Setting port to 0 will assign random free ports to all forked processes.",
"x-priority": "important"
},
"inspect": {
"oneOf": [
@@ -41,33 +43,44 @@
{ "type": "boolean" }
],
"description": "Ensures the app is starting with debugging.",
"default": "inspect"
"default": "inspect",
"x-priority": "important"
},
"runtimeArgs": {
"type": "array",
"description": "Extra args passed to the node process.",
"default": [],
"items": { "type": "string" }
"items": { "type": "string" },
"x-priority": "important"
},
"args": {
"type": "array",
"description": "Extra args when starting the app.",
"default": [],
"items": { "type": "string" }
"items": { "type": "string" },
"x-priority": "important"
},
"watch": {
"type": "boolean",
"description": "Enable re-building when files change.",
"default": true
"default": true,
"x-priority": "important"
},
"debounce": {
"type": "number",
"description": "Delay in milliseconds to wait before restarting. Useful to batch multiple file changes events together. Set to zero (0) to disable.",
"default": 500
"default": 500,
"x-priority": "important"
},
"runBuildTargetDependencies": {
"type": "boolean",
"description": "Whether to run dependencies before running the build. Set this to true if the project does not build libraries from source (e.g. 'buildLibsFromSource: false').",
"default": false
}
},
"additionalProperties": false,
"required": ["buildTarget"],
"examplesFile": "---\ntitle: JS Node executor examples\ndescription: This page contains examples for the @nx/js:node executor.\n---\n\nThe `@nx/js:node` executor runs the output of a build target. For example, an application uses esbuild ([`@nx/esbuild:esbuild`](/packages/esbuild/executors/esbuild)) to output the bundle to `dist/my-app` folder, which can then be executed by `@nx/js:node`.\n\n`project.json`:\n\n```json\n\"my-app\": {\n \"targets\": {\n \"serve\": {\n \"executor\": \"@nx/js:node\",\n \"options\": {\n \"buildTarget\": \"my-app:build\"\n }\n },\n \"build\": {\n \"executor\": \"@nx/esbuild:esbuild\",\n \"options\": {\n \"main\": \"my-app/src/main.ts\",\n \"output\": [\"dist/my-app\"],\n //...\n }\n },\n }\n}\n```\n\n```bash\nnpx nx serve my-app\n```\n\n## Examples\n\n{% tabs %}\n{% tab label=\"Pass extra Node CLI arguments\" %}\n\nUsing `runtimeArgs`, you can pass arguments to the underlying `node` command. For example, if you want to set [`--no-warnings`](https://nodejs.org/api/cli.html#--no-warnings) to silence all Node warnings, then add the following to the `project.json` file.\n\n```json\n\"my-app\": {\n \"targets\": {\n \"serve\": {\n \"executor\": \"@nx/js:node\",\n \"options\": {\n \"runtimeArgs\": [\"--no-warnings\"],\n //...\n },\n },\n }\n}\n```\n\n{% /tab %}\n\n{% tab label=\"Run all task dependencies\" %}\n\nIf your application build depends on other tasks, and you want those tasks to also be executed, then set the `runBuildTargetDependencies` to `true`. For example, a library may have a task to generate GraphQL schemas, which is consume by the application. In this case, you want to run the generate task before building and running the application.\n\nThis option is also useful when the build consumes a library from its output, not its source. For example, if an executor that supports `buildLibsFromSource` option has it set to `false` (e.g. [`@nx/webpack:webpack`](/packages/webpack/executors/webpack)).\n\nNote that this option will increase the build time, so use it only when necessary.\n\n```json\n\"my-app\": {\n \"targets\": {\n \"serve\": {\n \"executor\": \"@nx/js:node\",\n \"options\": {\n \"runBuildTargetDependencies\": true,\n //...\n },\n },\n }\n}\n```\n\n{% /tab %}\n\n{% /tabs %}\n",
"presets": []
},
"description": "Execute a Node application.",
@@ -18,7 +18,7 @@
"skipRemotes": {
"type": "array",
"items": { "type": "string" },
"description": "List of remote applications to not automatically serve, either statically or in development mode. This can be useful for multi-repository module federation setups where the host application uses a remote application from an external repository.",
"description": "List of remote applications to not automatically serve, either statically or in development mode.",
"x-priority": "important"
},
"buildTarget": {
@@ -34,7 +34,7 @@
"skipRemotes": {
"type": "array",
"items": { "type": "string" },
"description": "List of remote applications to not automatically serve, either statically or in development mode. This can be useful for multi-repository module federation setups where the host application uses a remote application from an external repository.",
"description": "List of remote applications to not automatically serve, either statically or in development mode.",
"x-priority": "important"
},
"host": {
+9
View File
@@ -189,6 +189,15 @@ describe('js e2e', () => {
`dist/libs/${lib}/src/lib/${lib}.js`,
`dist/libs/${lib}/src/lib/${lib}.d.ts`
);
// run a second time skipping the nx cache and with the outputs present
const secondBatchBuildOutput = runCLI(
`build ${parentLib} --skip-nx-cache`,
{ env: { NX_BATCH_MODE: 'true' } }
);
expect(secondBatchBuildOutput).toContain(
`Successfully ran target build for project ${parentLib} and 1 task it depends on`
);
}, 240_000);
it('should not create a `.babelrc` file when creating libs with js executors (--compiler=tsc)', () => {
+52
View File
@@ -5,9 +5,12 @@ import {
readFile,
runCLI,
runCLIAsync,
runCommandUntil,
waitUntil,
tmpProjPath,
uniq,
updateFile,
updateProjectConfig,
} from '@nx/e2e/utils';
import { execSync } from 'child_process';
@@ -47,5 +50,54 @@ describe('Node Applications + webpack', () => {
await runCLIAsync(`build ${app} --optimization`);
const optimizedContent = readFile(`dist/apps/${app}/main.js`);
expect(optimizedContent).toContain('console.log("foo "+"bar")');
// Test that serve can re-run dependency builds.
const lib = uniq('nodelib');
runCLI(`generate @nx/js:lib ${lib} --bundler=esbuild --no-interactive`);
updateProjectConfig(app, (config) => {
// Since we read from lib from dist, we should re-build it when lib changes.
config.targets.build.options.buildLibsFromSource = false;
config.targets.serve.options.runBuildTargetDependencies = true;
return config;
});
updateFile(
`apps/${app}/src/main.ts`,
`
import { ${lib} } from '@proj/${lib}';
console.log('Hello ' + ${lib}());
`
);
const serveProcess = await runCommandUntil(
`serve ${app} --watch --runBuildTargetDependencies`,
(output) => {
return output.includes(`Hello`);
}
);
// Update library source and check that it triggers rebuild.
const terminalOutputs: string[] = [];
serveProcess.stdout.on('data', (chunk) => {
const data = chunk.toString();
terminalOutputs.push(data);
});
updateFile(
`libs/${lib}/src/index.ts`,
`export function ${lib}() { return 'should rebuild lib'; }`
);
await waitUntil(
() => {
return terminalOutputs.some((output) =>
output.includes(`should rebuild lib`)
);
},
{ timeout: 30_000, ms: 200 }
);
serveProcess.kill();
}, 300_000);
});
+1 -1
View File
@@ -1,6 +1,6 @@
{
"packages": ["build/packages/*", "build/packages/nx/native-packages/*"],
"version": "16.5.0",
"version": "16.5.1",
"granularPathspec": false,
"command": {
"publish": {
+2 -1
View File
@@ -26,7 +26,8 @@
"webpack",
"http-server",
"magic-string",
"enquirer"
"enquirer",
"find-cache-dir"
],
"keepLifecycleScripts": true
}
+2 -1
View File
@@ -25,7 +25,8 @@
"webpack",
"http-server",
"magic-string",
"enquirer"
"enquirer",
"find-cache-dir"
],
"keepLifecycleScripts": true
}
+1
View File
@@ -49,6 +49,7 @@
"@typescript-eslint/type-utils": "^5.36.1",
"chalk": "^4.1.0",
"chokidar": "^3.5.1",
"find-cache-dir": "^3.3.2",
"http-server": "^14.1.0",
"ignore": "^5.0.4",
"magic-string": "~0.26.2",
@@ -1,5 +1,10 @@
import type { Schema } from './schema';
import { readCachedProjectGraph, workspaceRoot, Workspaces } from '@nx/devkit';
import {
logger,
readCachedProjectGraph,
workspaceRoot,
Workspaces,
} from '@nx/devkit';
import { scheduleTarget } from 'nx/src/adapter/ngcli-adapter';
import { executeWebpackDevServerBuilder } from '../webpack-dev-server/webpack-dev-server.impl';
import { readProjectsConfigurationFromProjectGraph } from 'nx/src/project-graph/project-graph';
@@ -51,6 +56,12 @@ export function executeModuleFederationDevServerBuilder(
const remotesToSkip = new Set(
findMatchingProjects(options.skipRemotes, projectGraph.nodes) ?? []
);
if (remotesToSkip.size > 0) {
logger.info(
`Remotes not served automatically: ${[...remotesToSkip].join(', ')}`
);
}
const staticRemotes = getStaticRemotes(
project,
context,
@@ -117,7 +117,7 @@
"items": {
"type": "string"
},
"description": "List of remote applications to not automatically serve, either statically or in development mode. This can be useful for multi-repository module federation setups where the host application uses a remote application from an external repository."
"description": "List of remote applications to not automatically serve, either statically or in development mode."
},
"pathToManifestFile": {
"type": "string",
@@ -73,7 +73,7 @@
"items": {
"type": "string"
},
"description": "List of remote applications to not automatically serve, either statically or in development mode. This can be useful for multi-repository module federation setups where the host application uses a remote application from an external repository."
"description": "List of remote applications to not automatically serve, either statically or in development mode."
},
"verbose": {
"type": "boolean",
@@ -1,7 +1,7 @@
import { ProjectConfiguration } from 'nx/src/config/workspace-json-project-json';
import { join } from 'path';
import { existsSync, readFileSync } from 'fs';
import { Remotes } from '@nx/devkit';
import { logger, Remotes } from '@nx/devkit';
export function getDynamicRemotes(
project: ProjectConfiguration,
@@ -45,19 +45,26 @@ export function getDynamicRemotes(
return [];
}
const dynamicRemotes = Object.entries(parsedManifest)
const allDynamicRemotes = Object.entries(parsedManifest)
.map(([remoteName]) => remoteName)
.filter((r) => !remotesToSkip.has(r));
const invalidDynamicRemotes = dynamicRemotes.filter(
(remote) => !workspaceProjects[remote]
);
if (invalidDynamicRemotes.length) {
throw new Error(
invalidDynamicRemotes.length === 1
? `Invalid dynamic remote configured in "${pathToManifestFile}": ${invalidDynamicRemotes[0]}.`
: `Invalid dynamic remotes configured in "${pathToManifestFile}": ${invalidDynamicRemotes.join(
', '
)}.`
const remotesNotInWorkspace: string[] = [];
const dynamicRemotes = allDynamicRemotes.filter((remote) => {
if (!workspaceProjects[remote]) {
remotesNotInWorkspace.push(remote);
return false;
}
return true;
});
if (remotesNotInWorkspace.length > 0) {
logger.warn(
`Skipping serving ${remotesNotInWorkspace.join(
', '
)} as they could not be found in the workspace. Ensure they are served correctly.`
);
}
@@ -89,22 +96,27 @@ export function getStaticRemotes(
Array.isArray(mfeConfig.remotes) && mfeConfig.remotes.length > 0
? mfeConfig.remotes
: [];
const staticRemotes = remotesConfig
const allStaticRemotes = remotesConfig
.map((remoteDefinition) =>
Array.isArray(remoteDefinition) ? remoteDefinition[0] : remoteDefinition
)
.filter((r) => !remotesToSkip.has(r));
const remotesNotInWorkspace: string[] = [];
const invalidStaticRemotes = staticRemotes.filter(
(remote) => !workspaceProjects[remote]
);
if (invalidStaticRemotes.length) {
throw new Error(
invalidStaticRemotes.length === 1
? `Invalid static remote configured in "${mfConfigPath}": ${invalidStaticRemotes[0]}.`
: `Invalid static remotes configured in "${mfConfigPath}": ${invalidStaticRemotes.join(
', '
)}.`
const staticRemotes = allStaticRemotes.filter((remote) => {
if (!workspaceProjects[remote]) {
remotesNotInWorkspace.push(remote);
return false;
}
return true;
});
if (remotesNotInWorkspace.length > 0) {
logger.warn(
`Skipping serving ${remotesNotInWorkspace.join(
', '
)} as they could not be found in the workspace. Ensure they are served correctly.`
);
}
@@ -23,7 +23,7 @@ export async function createSandbox(packageManager: PackageManager) {
`Installing dependencies with ${packageManager}`
).start();
const { install } = getPackageManagerCommand(packageManager);
const { install, preInstall } = getPackageManagerCommand(packageManager);
const tmpDir = dirSync().name;
try {
@@ -39,6 +39,10 @@ export async function createSandbox(packageManager: PackageManager) {
);
generatePackageManagerFiles(tmpDir, packageManager);
if (preInstall) {
await execAndWait(preInstall, tmpDir);
}
await execAndWait(install, tmpDir);
installSpinner.succeed();
@@ -36,6 +36,7 @@ export function getPackageManagerCommand(
): {
install: string;
exec: string;
preInstall?: string;
} {
const [pmMajor, pmMinor] =
getPackageManagerVersion(packageManager).split('.');
@@ -45,6 +46,9 @@ export function getPackageManagerCommand(
const useBerry = +pmMajor >= 2;
const installCommand = 'yarn install --silent';
return {
preInstall: useBerry
? 'yarn set version stable'
: 'yarn set version classic',
install: useBerry
? installCommand
: `${installCommand} --ignore-scripts`,
+1 -1
View File
@@ -5,5 +5,5 @@ export const cypressViteDevServerVersion = '^2.2.1';
export const cypressVersion = '^12.16.0';
export const cypressWebpackVersion = '^2.0.0';
export const webpackHttpPluginVersion = '^5.5.0';
export const viteVersion = '^4.3.4';
export const viteVersion = '~4.3.9';
export const htmlWebpackPluginVersion = '^5.5.0';
+9 -1
View File
@@ -464,12 +464,20 @@ export function ensurePackage<T extends any = any>(
console.log(`Fetching ${pkg}...`);
const packageManager = detectPackageManager();
const isVerbose = process.env.NX_VERBOSE_LOGGING === 'true';
const preInstallCommand = getPackageManagerCommand(packageManager).preInstall;
if (preInstallCommand) {
// ensure package.json and repo in tmp folder is set to a proper package manager state
execSync(preInstallCommand, {
cwd: tempDir,
stdio: isVerbose ? 'inherit' : 'ignore',
});
}
let addCommand = getPackageManagerCommand(packageManager).addDev;
if (packageManager === 'pnpm') {
addCommand = 'pnpm add -D'; // we need to ensure that we are not using workspace command
}
const isVerbose = process.env.NX_VERBOSE_LOGGING === 'true';
execSync(`${addCommand} ${pkg}@${requiredVersion}`, {
cwd: tempDir,
stdio: isVerbose ? 'inherit' : 'ignore',
@@ -9,6 +9,7 @@ import { satisfies } from 'semver';
import { getHelperDependenciesFromProjectGraph } from '@nx/js';
import {
getAllDependencies,
getPackageJson,
removePackageJsonFromFileMap,
} from '../utils/package-json-utils';
import { JSONLiteral } from 'jsonc-eslint-parser/lib/parser/ast';
@@ -126,13 +127,17 @@ export default createESLintRule<Options, MessageIds>({
projectGraph
);
const rootPackageJson = getPackageJson(join(workspaceRoot, 'package.json'));
// find all dependencies for the project
const npmDeps = findProjectsNpmDependencies(
sourceProject,
projectGraph,
buildTarget,
rootPackageJson,
{
helperDependencies: helperDependencies.map((dep) => dep.target),
isProduction: true,
},
removePackageJsonFromFileMap(projectFileMap)
);
@@ -148,12 +153,12 @@ export default createESLintRule<Options, MessageIds>({
'package.json'
);
globalThis.projPackageJsonDeps ??= getAllDependencies(projPackageJsonPath);
globalThis.projPackageJsonDeps ??= getAllDependencies(
getPackageJson(projPackageJsonPath)
);
const projPackageJsonDeps: Record<string, string> =
globalThis.projPackageJsonDeps;
const rootPackageJsonDeps = getAllDependencies(
join(workspaceRoot, 'package.json')
);
const rootPackageJsonDeps = getAllDependencies(rootPackageJson);
function validateMissingDependencies(node: AST.JSONProperty) {
if (!checkMissingDependencies) {
@@ -1,16 +1,22 @@
import { ProjectFileMap, readJsonFile } from '@nx/devkit';
import { existsSync } from 'fs';
import { PackageJson } from 'nx/src/utils/package-json';
export function getAllDependencies(path: string): Record<string, string> {
export function getAllDependencies(
packageJson: PackageJson
): Record<string, string> {
return {
...packageJson.dependencies,
...packageJson.devDependencies,
...packageJson.peerDependencies,
};
}
export function getPackageJson(path: string): PackageJson {
if (existsSync(path)) {
const packageJson = readJsonFile(path);
return {
...packageJson.dependencies,
...packageJson.devDependencies,
...packageJson.peerDependencies,
};
return readJsonFile(path);
}
return {};
return {} as PackageJson;
}
export function removePackageJsonFromFileMap(
+82
View File
@@ -0,0 +1,82 @@
---
title: JS Node executor examples
description: This page contains examples for the @nx/js:node executor.
---
The `@nx/js:node` executor runs the output of a build target. For example, an application uses esbuild ([`@nx/esbuild:esbuild`](/packages/esbuild/executors/esbuild)) to output the bundle to `dist/my-app` folder, which can then be executed by `@nx/js:node`.
`project.json`:
```json
"my-app": {
"targets": {
"serve": {
"executor": "@nx/js:node",
"options": {
"buildTarget": "my-app:build"
}
},
"build": {
"executor": "@nx/esbuild:esbuild",
"options": {
"main": "my-app/src/main.ts",
"output": ["dist/my-app"],
//...
}
},
}
}
```
```bash
npx nx serve my-app
```
## Examples
{% tabs %}
{% tab label="Pass extra Node CLI arguments" %}
Using `runtimeArgs`, you can pass arguments to the underlying `node` command. For example, if you want to set [`--no-warnings`](https://nodejs.org/api/cli.html#--no-warnings) to silence all Node warnings, then add the following to the `project.json` file.
```json
"my-app": {
"targets": {
"serve": {
"executor": "@nx/js:node",
"options": {
"runtimeArgs": ["--no-warnings"],
//...
},
},
}
}
```
{% /tab %}
{% tab label="Run all task dependencies" %}
If your application build depends on other tasks, and you want those tasks to also be executed, then set the `runBuildTargetDependencies` to `true`. For example, a library may have a task to generate GraphQL schemas, which is consume by the application. In this case, you want to run the generate task before building and running the application.
This option is also useful when the build consumes a library from its output, not its source. For example, if an executor that supports `buildLibsFromSource` option has it set to `false` (e.g. [`@nx/webpack:webpack`](/packages/webpack/executors/webpack)).
Note that this option will increase the build time, so use it only when necessary.
```json
"my-app": {
"targets": {
"serve": {
"executor": "@nx/js:node",
"options": {
"runBuildTargetDependencies": true,
//...
},
},
}
}
```
{% /tab %}
{% /tabs %}
-1
View File
@@ -12,7 +12,6 @@
"init": {
"factory": "./src/generators/init/init#initSchematic",
"schema": "./src/generators/init/schema.json",
"aliases": ["lib"],
"x-type": "init",
"description": "Initialize a TS/JS workspace.",
"hidden": true
+65 -41
View File
@@ -114,48 +114,67 @@ export async function* nodeExecutor(
childProcess: null,
promise: null,
start: async () => {
let buildFailed = false;
// Run the build
task.promise = new Promise<void>(async (resolve, reject) => {
task.childProcess = exec(
`npx nx run ${context.projectName}:${buildTarget.target}${
buildTarget.configuration ? `:${buildTarget.configuration}` : ''
}`,
if (options.runBuildTargetDependencies) {
// If task dependencies are to be run, then we need to run through CLI since `runExecutor` doesn't support it.
task.promise = new Promise<void>(async (resolve, reject) => {
task.childProcess = fork(
require.resolve('nx'),
[
'run',
`${context.projectName}:${buildTarget.target}${
buildTarget.configuration
? `:${buildTarget.configuration}`
: ''
}`,
],
{
cwd: context.root,
stdio: 'inherit',
}
);
task.childProcess.once('exit', (code) => {
if (code === 0) resolve();
else reject();
});
});
} else {
const output = await runExecutor(
buildTarget,
{
cwd: context.root,
...options.buildTargetOptions,
watch: false, // we'll handle the watch in this executor
},
(error, stdout, stderr) => {
if (
// Build succeeded
!error ||
// If task was killed then another build process has started, ignore errors.
task.killed
) {
resolve();
return;
}
logger.info(stdout);
buildFailed = true;
if (options.watch) {
logger.error(
`Build failed, waiting for changes to restart...`
);
resolve(); // Don't reject because it'll error out and kill the Nx process.
} else {
logger.error(`Build failed. See above for errors.`);
reject();
}
}
context
);
});
task.promise = new Promise(async (resolve, reject) => {
let error = false;
let event;
do {
event = await output.next();
if (event.value?.success === false) {
error = true;
}
} while (!event.done);
if (error) reject();
else resolve();
});
}
// Wait for build to finish
await task.promise;
// Wait for build to finish.
try {
await task.promise;
} catch {
// If in watch-mode, don't throw or else the process exits.
if (options.watch) {
logger.error(`Build failed, waiting for changes to restart...`);
return;
} else {
throw new Error(`Build failed. See above for errors.`);
}
}
// Task may have been stopped due to another running task.
// OR build failed, so don't start the process.
if (task.killed || buildFailed) return;
// Before running the program, check if the task has been killed (by a new change during watch).
if (task.killed) return;
// Run the program
task.promise = new Promise<void>((resolve, reject) => {
@@ -173,16 +192,17 @@ export async function* nodeExecutor(
}
);
task.childProcess.stderr.on('data', (data) => {
const handleStdErr = (data) => {
// Don't log out error if task is killed and new one has started.
// This could happen if a new build is triggered while new process is starting, since the operation is not atomic.
// Log the error in normal mode
if (!options.watch || !task.killed) {
logger.error(data.toString());
}
});
};
task.childProcess.stderr.on('data', handleStdErr);
task.childProcess.once('exit', (code) => {
task.childProcess.off('data', handleStdErr);
if (options.watch && !task.killed) {
logger.info(
`NX Process exited with code ${code}, waiting for changes to restart...`
@@ -203,7 +223,11 @@ export async function* nodeExecutor(
if (task.childProcess) {
await killTree(task.childProcess.pid, signal);
}
await task.promise;
try {
await task.promise;
} catch {
// Doesn't matter if task fails, we just need to wait until it finishes.
}
},
};
+1
View File
@@ -14,4 +14,5 @@ export interface NodeExecutorOptions {
port: number;
watch?: boolean;
debounce?: number;
runBuildTargetDependencies?: boolean;
}
+21 -8
View File
@@ -27,12 +27,14 @@
"host": {
"type": "string",
"default": "localhost",
"description": "The host to inspect the process on."
"description": "The host to inspect the process on.",
"x-priority": "important"
},
"port": {
"type": "number",
"default": 9229,
"description": "The port to inspect the process on. Setting port to 0 will assign random free ports to all forked processes."
"description": "The port to inspect the process on. Setting port to 0 will assign random free ports to all forked processes.",
"x-priority": "important"
},
"inspect": {
"oneOf": [
@@ -45,7 +47,8 @@
}
],
"description": "Ensures the app is starting with debugging.",
"default": "inspect"
"default": "inspect",
"x-priority": "important"
},
"runtimeArgs": {
"type": "array",
@@ -53,7 +56,8 @@
"default": [],
"items": {
"type": "string"
}
},
"x-priority": "important"
},
"args": {
"type": "array",
@@ -61,19 +65,28 @@
"default": [],
"items": {
"type": "string"
}
},
"x-priority": "important"
},
"watch": {
"type": "boolean",
"description": "Enable re-building when files change.",
"default": true
"default": true,
"x-priority": "important"
},
"debounce": {
"type": "number",
"description": "Delay in milliseconds to wait before restarting. Useful to batch multiple file changes events together. Set to zero (0) to disable.",
"default": 500
"default": 500,
"x-priority": "important"
},
"runBuildTargetDependencies": {
"type": "boolean",
"description": "Whether to run dependencies before running the build. Set this to true if the project does not build libraries from source (e.g. 'buildLibsFromSource: false').",
"default": false
}
},
"additionalProperties": false,
"required": ["buildTarget"]
"required": ["buildTarget"],
"examplesFile": "../../../docs/node-examples.md"
}
+73 -26
View File
@@ -76,6 +76,20 @@ export async function* tscBatchExecutor(
},
};
const processTaskPostCompilation = (tsConfig: string) => {
if (tsConfigTaskInfoMap[tsConfig]) {
const taskInfo = tsConfigTaskInfoMap[tsConfig];
taskInfo.assetsHandler.processAllAssetsOnceSync();
updatePackageJson(
taskInfo.options,
taskInfo.context,
taskInfo.projectGraphNode,
taskInfo.buildableProjectNodeDependencies
);
taskInfo.endTime = Date.now();
}
};
const typescriptCompilation = compileTypescriptSolution(
tsCompilationContext,
shouldWatch,
@@ -86,19 +100,7 @@ export async function* tscBatchExecutor(
tsConfigTaskInfoMap[tsConfig].startTime = Date.now();
}
},
afterProjectCompilationCallback: (tsConfig) => {
if (tsConfigTaskInfoMap[tsConfig]) {
const taskInfo = tsConfigTaskInfoMap[tsConfig];
taskInfo.assetsHandler.processAllAssetsOnceSync();
updatePackageJson(
taskInfo.options,
taskInfo.context,
taskInfo.projectGraphNode,
taskInfo.buildableProjectNodeDependencies
);
taskInfo.endTime = Date.now();
}
},
afterProjectCompilationCallback: processTaskPostCompilation,
}
);
@@ -136,23 +138,68 @@ export async function* tscBatchExecutor(
});
}
return yield* mapAsyncIterable(typescriptCompilation, async (iterator) => {
const { value, done } = await iterator.next();
const toBatchExecutorTaskResult = (
tsConfig: string,
success: boolean
): BatchExecutorTaskResult => ({
task: tsConfigTaskInfoMap[tsConfig].task,
result: {
success: success,
terminalOutput: tsConfigTaskInfoMap[tsConfig].terminalOutput,
startTime: tsConfigTaskInfoMap[tsConfig].startTime,
endTime: tsConfigTaskInfoMap[tsConfig].endTime,
},
});
let isCompilationDone = false;
const taskTsConfigsToReport = new Set(
Object.keys(taskGraph.tasks).map((t) => taskInMemoryTsConfigMap[t].path)
);
let tasksToReportIterator: IterableIterator<string>;
const processSkippedTasks = () => {
const { value: tsConfig, done } = tasksToReportIterator.next();
if (done) {
return { value, done: true };
return { value: undefined, done: true };
}
const taskResult: BatchExecutorTaskResult = {
task: tsConfigTaskInfoMap[value.tsConfig].task,
result: {
success: value.success,
terminalOutput: tsConfigTaskInfoMap[value.tsConfig].terminalOutput,
startTime: tsConfigTaskInfoMap[value.tsConfig].startTime,
endTime: tsConfigTaskInfoMap[value.tsConfig].endTime,
},
};
tsConfigTaskInfoMap[tsConfig].startTime = Date.now();
processTaskPostCompilation(tsConfig);
return { value: taskResult, done: false };
return { value: toBatchExecutorTaskResult(tsConfig, true), done: false };
};
return yield* mapAsyncIterable(typescriptCompilation, async (iterator) => {
if (isCompilationDone) {
return processSkippedTasks();
}
const { value, done } = await iterator.next();
if (done) {
if (taskTsConfigsToReport.size > 0) {
/**
* TS compilation is done but we still have tasks to report. This can
* happen if, for example, a project is identified as affected, but
* no file in the TS project is actually changed or if running a
* task with `--skip-nx-cache` and the outputs are already there. There
* can still be changes to assets or other files we need to process.
*
* Switch to handle the iterator for the tasks we still need to report.
*/
isCompilationDone = true;
tasksToReportIterator = taskTsConfigsToReport.values();
return processSkippedTasks();
}
return { value: undefined, done: true };
}
taskTsConfigsToReport.delete(value.tsConfig);
return {
value: toBatchExecutorTaskResult(value.tsConfig, value.success),
done: false,
};
});
}
@@ -131,7 +131,7 @@ function setupNpm(options: VerdaccioExecutorSchema) {
return () => {};
}
let npmRegistryPath;
let npmRegistryPath: string;
try {
npmRegistryPath = execSync(
`npm config get registry --location ${options.location}`
@@ -154,7 +154,13 @@ function setupNpm(options: VerdaccioExecutorSchema) {
return () => {
try {
if (npmRegistryPath) {
const currentNpmRegistryPath = execSync(
`npm config get registry --location ${options.location}`
)
?.toString()
?.trim()
?.replace('\u001b[2K\u001b[1G', ''); // strip out ansi codes
if (npmRegistryPath && currentNpmRegistryPath.includes('localhost')) {
execSync(
`npm config set registry ${npmRegistryPath} --location ${options.location}`
);
@@ -240,7 +246,13 @@ function setupYarn(options: VerdaccioExecutorSchema) {
return () => {
try {
if (yarnRegistryPath) {
const currentYarnRegistryPath = execSync(
`yarn config get ${registryConfigName}`
)
?.toString()
?.trim()
?.replace('\u001b[2K\u001b[1G', ''); // strip out ansi codes
if (yarnRegistryPath && currentYarnRegistryPath.includes('localhost')) {
execSync(
`yarn config set ${registryConfigName} ${yarnRegistryPath}` +
(options.location === 'user' ? ' --home' : '')
@@ -290,7 +290,7 @@ describe('updatePackageJson', () => {
const fileMap = {
'@org/lib1': [
{
file: 'test.ts',
file: 'libs/lib1/src/test.ts',
hash: '',
deps: ['npm:external1', 'npm:external2'],
},
@@ -1,6 +1,7 @@
{
"name": "@nx/nx-darwin-arm64",
"version": "0.0.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/nrwl/nx.git",
@@ -1,6 +1,7 @@
{
"name": "@nx/nx-darwin-x64",
"version": "0.0.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/nrwl/nx.git",
@@ -1,6 +1,7 @@
{
"name": "@nx/nx-freebsd-x64",
"version": "0.0.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/nrwl/nx.git",
@@ -1,6 +1,7 @@
{
"name": "@nx/nx-linux-arm-gnueabihf",
"version": "0.0.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/nrwl/nx.git",
@@ -1,6 +1,7 @@
{
"name": "@nx/nx-linux-arm64-gnu",
"version": "0.0.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/nrwl/nx.git",
@@ -1,6 +1,7 @@
{
"name": "@nx/nx-linux-arm64-musl",
"version": "0.0.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/nrwl/nx.git",
@@ -1,6 +1,7 @@
{
"name": "@nx/nx-linux-x64-gnu",
"version": "0.0.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/nrwl/nx.git",
@@ -1,6 +1,7 @@
{
"name": "@nx/nx-linux-x64-musl",
"version": "0.0.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/nrwl/nx.git",
@@ -1,6 +1,7 @@
{
"name": "@nx/nx-win32-arm64-msvc",
"version": "0.0.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/nrwl/nx.git",
@@ -1,6 +1,7 @@
{
"name": "@nx/nx-win32-x64-msvc",
"version": "0.0.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/nrwl/nx.git",
+138
View File
@@ -1161,6 +1161,144 @@ describe('TaskHasher', () => {
expect(hash.value).toContain('|5.0.0|');
});
it('should hash entire subtree of dependencies', async () => {
const createHasher = () =>
new InProcessTaskHasher(
{},
allWorkspaceFiles,
{
nodes: {
appA: {
name: 'appA',
type: 'app',
data: {
root: 'apps/appA',
targets: { build: { executor: '@nx/webpack:webpack' } },
},
},
appB: {
name: 'appB',
type: 'app',
data: {
root: 'apps/appB',
targets: { build: { executor: '@nx/webpack:webpack' } },
},
},
},
externalNodes: {
'npm:packageA': {
name: 'npm:packageA',
type: 'npm',
data: {
packageName: 'packageA',
version: '0.0.0',
hash: '$packageA0.0.0$',
},
},
'npm:packageB': {
name: 'npm:packageB',
type: 'npm',
data: {
packageName: 'packageB',
version: '0.0.0',
hash: '$packageB0.0.0$',
},
},
'npm:packageC': {
name: 'npm:packageC',
type: 'npm',
data: {
packageName: 'packageC',
version: '0.0.0',
hash: '$packageC0.0.0$',
},
},
},
dependencies: {
appA: [
{
source: 'app',
target: 'npm:packageA',
type: DependencyType.static,
},
{
source: 'app',
target: 'npm:packageB',
type: DependencyType.static,
},
{
source: 'app',
target: 'npm:packageC',
type: DependencyType.static,
},
],
appB: [
{
source: 'app',
target: 'npm:packageC',
type: DependencyType.static,
},
],
'npm:packageC': [
{
source: 'app',
target: 'npm:packageA',
type: DependencyType.static,
},
{
source: 'app',
target: 'npm:packageB',
type: DependencyType.static,
},
],
'npm:packageB': [
{
source: 'app',
target: 'npm:packageA',
type: DependencyType.static,
},
],
},
},
{
roots: ['app-build'],
tasks: {
'app-build': {
id: 'app-build',
target: { project: 'app', target: 'build' },
overrides: {},
},
},
dependencies: {},
},
{} as any,
{},
fileHasher
);
const computeTaskHash = async (hasher, appName) => {
const hashAppA = await hasher.hashTask({
target: { project: appName, target: 'build' },
id: `${appName}-build`,
overrides: { prop: 'prop-value' },
});
return hashAppA.value;
};
const hasher1 = createHasher();
await computeTaskHash(hasher1, 'appA');
const hashAppB1 = await computeTaskHash(hasher1, 'appB');
const hasher2 = createHasher();
const hashAppB2 = await computeTaskHash(hasher2, 'appB');
await computeTaskHash(hasher2, 'appA');
expect(hashAppB1).toEqual(hashAppB2);
});
it('should not hash when nx:run-commands executor', async () => {
const hasher = new InProcessTaskHasher(
{},
+37 -14
View File
@@ -332,7 +332,7 @@ class TaskHasherImpl {
visited
);
} else {
const hash = this.hashExternalDependency(d.target);
const hash = this.hashExternalDependency(d.source, d.target);
return {
value: hash,
details: {
@@ -408,16 +408,29 @@ class TaskHasherImpl {
return partialHashes;
}
private computeExternalDependencyIdentifier(
sourceProjectName: string,
targetProjectName: string
): `${string}->${string}` {
return `${sourceProjectName}->${targetProjectName}`;
}
private hashExternalDependency(
projectName: string,
sourceProjectName: string,
targetProjectName: string,
visited = new Set<string>()
): string {
// try to retrieve the hash from cache
if (this.externalDepsHashCache[projectName]) {
return this.externalDepsHashCache[projectName];
if (this.externalDepsHashCache[targetProjectName]) {
return this.externalDepsHashCache[targetProjectName];
}
visited.add(projectName);
const node = this.projectGraph.externalNodes[projectName];
visited.add(
this.computeExternalDependencyIdentifier(
sourceProjectName,
targetProjectName
)
);
const node = this.projectGraph.externalNodes[targetProjectName];
let partialHash: string;
if (node) {
const partialHashes: string[] = [];
@@ -429,22 +442,32 @@ class TaskHasherImpl {
partialHashes.push(node.data.version);
}
// we want to calculate the hash of the entire dependency tree
if (this.projectGraph.dependencies[projectName]) {
this.projectGraph.dependencies[projectName].forEach((d) => {
if (!visited.has(d.target)) {
partialHashes.push(this.hashExternalDependency(d.target, visited));
if (this.projectGraph.dependencies[targetProjectName]) {
this.projectGraph.dependencies[targetProjectName].forEach((d) => {
if (
!visited.has(
this.computeExternalDependencyIdentifier(
targetProjectName,
d.target
)
)
) {
partialHashes.push(
this.hashExternalDependency(targetProjectName, d.target, visited)
);
}
});
}
partialHash = hashArray(partialHashes);
} else {
// unknown dependency
// this may occur if dependency is not an npm package
// but rather symlinked in node_modules or it's pointing to a remote git repo
// in this case we have no information about the versioning of the given package
partialHash = `__${projectName}__`;
partialHash = `__${targetProjectName}__`;
}
this.externalDepsHashCache[projectName] = partialHash;
this.externalDepsHashCache[targetProjectName] = partialHash;
return partialHash;
}
@@ -470,7 +493,7 @@ class TaskHasherImpl {
const executorPackage = target.executor.split(':')[0];
const executorNodeName =
this.findExternalDependencyNodeName(executorPackage);
hash = this.hashExternalDependency(executorNodeName);
hash = this.hashExternalDependency(projectName, executorNodeName);
} else {
// use command external dependencies if available to construct the hash
const partialHashes: string[] = [];
@@ -482,7 +505,7 @@ class TaskHasherImpl {
const externalDependencies = input['externalDependencies'];
for (let dep of externalDependencies) {
dep = this.findExternalDependencyNodeName(dep);
partialHashes.push(this.hashExternalDependency(dep));
partialHashes.push(this.hashExternalDependency(projectName, dep));
}
}
}
@@ -43,11 +43,19 @@ export function createPackageJson(
const projectNode = graph.nodes[projectName];
const isLibrary = projectNode.type === 'lib';
const rootPackageJson = readJsonFile(
`${options.root || workspaceRoot}/package.json`
);
const npmDeps = findProjectsNpmDependencies(
projectNode,
graph,
options.target,
{ helperDependencies: options.helperDependencies },
rootPackageJson,
{
helperDependencies: options.helperDependencies,
isProduction: options.isProduction,
},
fileMap
);
@@ -92,9 +100,6 @@ export function createPackageJson(
);
};
const rootPackageJson = readJsonFile(
`${options.root || workspaceRoot}/package.json`
);
Object.entries(npmDeps.dependencies).forEach(([packageName, version]) => {
if (
rootPackageJson.devDependencies?.[packageName] &&
@@ -179,9 +184,11 @@ export function findProjectsNpmDependencies(
projectNode: ProjectGraphProjectNode,
graph: ProjectGraph,
target: string,
rootPackageJson: PackageJson,
options: {
helperDependencies?: string[];
ignoredDependencies?: string[];
isProduction?: boolean;
},
fileMap?: ProjectFileMap
): NpmDeps {
@@ -208,13 +215,22 @@ export function findProjectsNpmDependencies(
recursivelyCollectPeerDependencies(dep, graph, npmDeps, seen);
});
// if it's production, we want to ignore all found devDependencies
const ignoredDependencies =
options.isProduction && rootPackageJson.devDependencies
? [
...(options.ignoredDependencies || []),
...Object.keys(rootPackageJson.devDependencies),
]
: options.ignoredDependencies || [];
findAllNpmDeps(
fileMap,
projectNode,
graph,
npmDeps,
seen,
options.ignoredDependencies || [],
ignoredDependencies,
dependencyInputs,
selfInputs
);
+1
View File
@@ -1,6 +1,7 @@
import { workspaceRoot } from '../utils/workspace-root';
import { mkdir, mkdirSync, pathExists, readFile, writeFile } from 'fs-extra';
import { join } from 'path';
import { performance } from 'perf_hooks';
import { DefaultTasksRunnerOptions } from './default-tasks-runner';
import { spawn } from 'child_process';
import { cacheDir } from '../utils/cache-directory';
@@ -388,10 +388,10 @@ export function createTaskGraph(
}
export function mapTargetDefaultsToDependencies(
defaults: TargetDefaults
defaults: TargetDefaults | undefined
): TargetDependencies {
const res = {};
Object.keys(defaults).forEach((k) => {
Object.keys(defaults ?? {}).forEach((k) => {
res[k] = defaults[k].dependsOn;
});
@@ -2,8 +2,8 @@ import {
findMatchingProjects,
getMatchingStringsWithCache,
} from './find-matching-projects';
import minimatch = require('minimatch');
import type { ProjectGraphProjectNode } from '../config/project-graph';
import minimatch = require('minimatch');
describe('findMatchingProjects', () => {
let projectGraph: Record<string, ProjectGraphProjectNode> = {
+4
View File
@@ -15,6 +15,7 @@ const execAsync = promisify(exec);
export type PackageManager = 'yarn' | 'pnpm' | 'npm';
export interface PackageManagerCommands {
preInstall?: string;
install: string;
ciInstall: string;
add: string;
@@ -64,6 +65,9 @@ export function getPackageManagerCommand(
const useBerry = gte(yarnVersion, '2.0.0');
return {
preInstall: useBerry
? 'yarn set version stable'
: 'yarn set version classic',
install: 'yarn',
ciInstall: useBerry
? 'yarn install --immutable'
+9
View File
@@ -950,6 +950,15 @@
"alwaysAddToPackageJson": false
}
}
},
"16.5.0-beta.4": {
"version": "16.5.0-beta.4",
"packages": {
"@types/react": {
"version": "18.2.14",
"alwaysAddToPackageJson": false
}
}
}
}
}
@@ -39,12 +39,39 @@ export default async function* moduleFederationDevServer(
}
const remotesToSkip = new Set(
findMatchingProjects(options.skipRemotes ?? [], context.projectGraph.nodes)
findMatchingProjects(options.skipRemotes, context.projectGraph.nodes) ?? []
);
if (remotesToSkip.size > 0) {
logger.info(
`Remotes not served automatically: ${[...remotesToSkip.values()].join(
', '
)}`
);
}
const remotesNotInWorkspace: string[] = [];
const knownRemotes = (moduleFederationConfig.remotes ?? []).filter((r) => {
const validRemote = Array.isArray(r) ? r[0] : r;
return !remotesToSkip.has(validRemote);
if (remotesToSkip.has(validRemote)) {
return false;
} else if (!context.projectGraph.nodes[validRemote]) {
remotesNotInWorkspace.push(validRemote);
return false;
} else {
return true;
}
});
if (remotesNotInWorkspace.length > 0) {
logger.warn(
`Skipping serving ${remotesNotInWorkspace.join(
', '
)} as they could not be found in the workspace. Ensure they are served correctly.`
);
}
const remotePorts = knownRemotes.map(
(r) => context.projectGraph.nodes[r].data.targets['serve'].options.port
);
@@ -19,7 +19,7 @@
"items": {
"type": "string"
},
"description": "List of remote applications to not automatically serve, either statically or in development mode. This can be useful for multi-repository module federation setups where the host application uses a remote application from an external repository.",
"description": "List of remote applications to not automatically serve, either statically or in development mode.",
"x-priority": "important"
},
"buildTarget": {
@@ -46,9 +46,27 @@ export default async function* moduleFederationSsrDevServer(
}
const remotesToSkip = new Set(options.skipRemotes ?? []);
const knownRemotes = (moduleFederationConfig.remotes ?? []).filter(
(r) => !remotesToSkip.has(r)
);
const remotesNotInWorkspace: string[] = [];
const knownRemotes = (moduleFederationConfig.remotes ?? []).filter((r) => {
const validRemote = Array.isArray(r) ? r[0] : r;
if (remotesToSkip.has(validRemote)) {
return false;
} else if (!context.projectGraph.nodes[validRemote]) {
remotesNotInWorkspace.push(validRemote);
return false;
} else {
return true;
}
});
if (remotesNotInWorkspace.length > 0) {
logger.warn(
`Skipping serving ${remotesNotInWorkspace.join(
', '
)} as they could not be found in the workspace. Ensure they are served correctly.`
);
}
const devServeApps = !options.devRemotes
? []
@@ -35,7 +35,7 @@
"items": {
"type": "string"
},
"description": "List of remote applications to not automatically serve, either statically or in development mode. This can be useful for multi-repository module federation setups where the host application uses a remote application from an external repository.",
"description": "List of remote applications to not automatically serve, either statically or in development mode.",
"x-priority": "important"
},
"host": {
+1 -1
View File
@@ -8,7 +8,7 @@ export const reactDomVersion = '18.2.0';
export const reactIsVersion = '18.2.0';
export const swcLoaderVersion = '0.1.15';
export const babelLoaderVersion = '^9.1.2';
export const typesReactVersion = '18.2.13';
export const typesReactVersion = '18.2.14';
export const typesReactDomVersion = '18.2.6';
export const typesReactIsVersion = '18.2.1';
@@ -564,7 +564,7 @@ export function getTsConfigPath(
const { root, projectType } = readProjectConfiguration(tree, projectName);
return join(
root,
path && path.length > 0
path?.length > 0
? path
: projectType === 'application'
? 'tsconfig.app.json'
@@ -595,7 +595,7 @@ export function addBuildStorybookToCacheableOperations(tree: Tree) {
}
export function projectIsRootProjectInStandaloneWorkspace(projectRoot: string) {
return relative(workspaceRoot, projectRoot).length === 0;
return relative(workspaceRoot, projectRoot)?.length === 0;
}
export function workspaceHasRootProject(tree: Tree) {
@@ -675,21 +675,21 @@ export function renameAndMoveOldTsConfig(
pathToStorybookConfigFile: string,
tree: Tree
) {
if (pathToStorybookConfigFile) {
if (pathToStorybookConfigFile && tree.exists(pathToStorybookConfigFile)) {
updateJson(tree, pathToStorybookConfigFile, (json) => {
if (json.extends?.startsWith('../')) {
// drop one level of nesting
json.extends = json.extends.replace('../', './');
}
for (let i = 0; i < json.files.length; i++) {
for (let i = 0; i < json.files?.length; i++) {
// drop one level of nesting
if (json.files[i].startsWith('../../../')) {
json.files[i] = json.files[i].replace('../../../', '../../');
}
}
for (let i = 0; i < json.include.length; i++) {
for (let i = 0; i < json.include?.length; i++) {
if (json.include[i].startsWith('../')) {
json.include[i] = json.include[i].replace('../', '');
}
@@ -702,7 +702,7 @@ export function renameAndMoveOldTsConfig(
}
}
for (let i = 0; i < json.exclude.length; i++) {
for (let i = 0; i < json.exclude?.length; i++) {
if (json.exclude[i].startsWith('../')) {
json.exclude[i] = json.exclude[i].replace('../', 'src/');
}
@@ -718,8 +718,13 @@ export function renameAndMoveOldTsConfig(
}
const projectTsConfig = joinPathFragments(projectRoot, 'tsconfig.json');
if (!tree.exists(projectTsConfig)) {
return;
}
updateJson(tree, projectTsConfig, (json) => {
for (let i = 0; i < json.references.length; i++) {
for (let i = 0; i < json.references?.length; i++) {
if (json.references[i].path === './.storybook/tsconfig.json') {
json.references[i].path = './tsconfig.storybook.json';
break;
@@ -88,6 +88,24 @@ describe('testing utilities', () => {
`
);
appTree.write(
`test-ui-lib/src/lib/button/button.other.stories.ts`,
`
import type { Meta } from '@storybook/react';
import { Button } from './button';
const Story: Meta<typeof Button> = {
component: Button,
title: 'Layout/Texts/Button',
};
export default Story;
export const Primary = {
args: {},
};
`
);
appTree.write(
`test-ui-lib/src/lib/button/button.component.other.ts`,
`
@@ -155,6 +173,14 @@ describe('testing utilities', () => {
expect(fileIsStory).toBeTruthy();
});
it('should verify it is story when using Meta', () => {
const fileIsStory = isTheFileAStory(
appTree,
'test-ui-lib/src/lib/button/button.other.stories.ts'
);
expect(fileIsStory).toBeTruthy();
});
it('should verify it is story for ReactNative', () => {
const fileIsStory = isTheFileAStory(
appTree,
@@ -221,6 +221,7 @@ export function isTheFileAStory(tree: Tree, path: string): boolean {
importSpecifiers.forEach((importSpecifier: ts.ImportSpecifier) => {
if (
importSpecifier.getText() === 'Story' ||
importSpecifier.getText() === 'Meta' ||
importSpecifier.getText() === 'storiesOf' ||
importSpecifier.getText() === 'ComponentStory'
) {
+1 -1
View File
@@ -7,4 +7,4 @@ export const tsNodeVersion = '10.9.1';
export const storybookVersion = '^7.0.24';
export const reactVersion = '^18.2.0';
export const viteVersion = '^4.3.4';
export const viteVersion = '~4.3.9';
@@ -8,14 +8,14 @@ exports[`@nx/vite:init dependencies for package.json should add vite packages an
"devDependencies": {
"@nx/js": "0.0.1",
"@nx/vite": "0.0.1",
"@vitejs/plugin-react": "^4.0.0",
"@vitest/ui": "^0.32.0",
"@vitejs/plugin-react": "~4.0.0",
"@vitest/ui": "~0.32.0",
"existing": "1.0.0",
"prettier": "^2.6.2",
"typescript": "~5.1.3",
"vite": "^4.3.9",
"vite-tsconfig-paths": "^4.2.0",
"vitest": "^0.32.0",
"vite": "~4.3.9",
"vite-tsconfig-paths": "~4.2.0",
"vitest": "~0.32.0",
},
"name": "test-name",
}
@@ -28,12 +28,12 @@ exports[`@nx/vite:init dependencies for package.json should support --testEnviro
"@edge-runtime/vm": "~3.0.2",
"@nx/js": "0.0.1",
"@nx/vite": "0.0.1",
"@vitest/ui": "^0.32.0",
"@vitest/ui": "~0.32.0",
"prettier": "^2.6.2",
"typescript": "~5.1.3",
"vite": "^4.3.9",
"vite-tsconfig-paths": "^4.2.0",
"vitest": "^0.32.0",
"vite": "~4.3.9",
"vite-tsconfig-paths": "~4.2.0",
"vitest": "~0.32.0",
},
"name": "test-name",
}
@@ -45,13 +45,13 @@ exports[`@nx/vite:init dependencies for package.json should support --testEnviro
"devDependencies": {
"@nx/js": "0.0.1",
"@nx/vite": "0.0.1",
"@vitest/ui": "^0.32.0",
"@vitest/ui": "~0.32.0",
"happy-dom": "~9.20.3",
"prettier": "^2.6.2",
"typescript": "~5.1.3",
"vite": "^4.3.9",
"vite-tsconfig-paths": "^4.2.0",
"vitest": "^0.32.0",
"vite": "~4.3.9",
"vite-tsconfig-paths": "~4.2.0",
"vitest": "~0.32.0",
},
"name": "test-name",
}
@@ -63,13 +63,13 @@ exports[`@nx/vite:init dependencies for package.json should support --testEnviro
"devDependencies": {
"@nx/js": "0.0.1",
"@nx/vite": "0.0.1",
"@vitest/ui": "^0.32.0",
"@vitest/ui": "~0.32.0",
"jsdom": "~22.1.0",
"prettier": "^2.6.2",
"typescript": "~5.1.3",
"vite": "^4.3.9",
"vite-tsconfig-paths": "^4.2.0",
"vitest": "^0.32.0",
"vite": "~4.3.9",
"vite-tsconfig-paths": "~4.2.0",
"vitest": "~0.32.0",
},
"name": "test-name",
}
+8 -8
View File
@@ -1,15 +1,15 @@
export const nxVersion = require('../../package.json').version;
export const viteVersion = '^4.3.9';
export const vitestVersion = '^0.32.0';
export const vitestUiVersion = '^0.32.0';
export const vitePluginReactVersion = '^4.0.0';
export const vitePluginReactSwcVersion = '^3.3.2';
export const viteTsConfigPathsVersion = '^4.2.0';
export const viteVersion = '~4.3.9';
export const vitestVersion = '~0.32.0';
export const vitestUiVersion = '~0.32.0';
export const vitePluginReactVersion = '~4.0.0';
export const vitePluginReactSwcVersion = '~3.3.2';
export const viteTsConfigPathsVersion = '~4.2.0';
export const jsdomVersion = '~22.1.0';
export const vitePluginDtsVersion = '~2.3.0';
export const happyDomVersion = '~9.20.3';
export const edgeRuntimeVmVersion = '~3.0.2';
// Coverage providers
export const vitestCoverageC8Version = '^0.32.0';
export const vitestCoverageIstanbulVersion = '^0.32.0';
export const vitestCoverageC8Version = '~0.32.0';
export const vitestCoverageIstanbulVersion = '~0.32.0';
+13 -1
View File
@@ -20,6 +20,18 @@ import browserslist = require('browserslist');
const VALID_BROWSERSLIST_FILES = ['.browserslistrc', 'browserslist'];
const ES5_BROWSERS = [
'ie 10',
'ie 11',
'safari 11',
'safari 11.1',
'safari 12',
'safari 12.1',
'safari 13',
'ios_saf 13.0',
'ios_saf 13.3',
];
function getTerserEcmaVersion(projectRoot: string) {
let pathToBrowserslistFile = '';
for (const browserslistFile of VALID_BROWSERSLIST_FILES) {
@@ -36,7 +48,7 @@ function getTerserEcmaVersion(projectRoot: string) {
const env = browserslist.loadConfig({ path: pathToBrowserslistFile });
const browsers = browserslist(env);
return browsers.includes('ie 11') ? 5 : 2020;
return browsers.some((b) => ES5_BROWSERS.includes(b)) ? 5 : 2020;
}
const IGNORED_WEBPACK_WARNINGS = [
-1
View File
@@ -36,7 +36,6 @@ const IGNORE_MATCHES_IN_PACKAGE = {
'injection-js',
'browserslist',
'cacache',
'find-cache-dir',
'less',
'node-sass',
'node-sass-tilde-importer',