Compare commits
20 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7b7f1e7f72 | |||
| f525ba37ee | |||
| 5e8f9f4b1e | |||
| 13aa3be1f0 | |||
| f9a615bb04 | |||
| 1a0e65a3d3 | |||
| 25263f72d8 | |||
| 9e448fe341 | |||
| 802243e410 | |||
| 0c480fd94d | |||
| b1a8917b8d | |||
| 0e15beafb2 | |||
| 66090ca3c9 | |||
| 97ce47aa69 | |||
| b39cafaf86 | |||
| ea28f16faf | |||
| 0d04949ffd | |||
| ed22afecef | |||
| a496ef9144 | |||
| 699af690b7 |
+18
-10
@@ -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",
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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)', () => {
|
||||
|
||||
@@ -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
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"packages": ["build/packages/*", "build/packages/nx/native-packages/*"],
|
||||
"version": "16.5.0",
|
||||
"version": "16.5.1",
|
||||
"granularPathspec": false,
|
||||
"command": {
|
||||
"publish": {
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
"webpack",
|
||||
"http-server",
|
||||
"magic-string",
|
||||
"enquirer"
|
||||
"enquirer",
|
||||
"find-cache-dir"
|
||||
],
|
||||
"keepLifecycleScripts": true
|
||||
}
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
"webpack",
|
||||
"http-server",
|
||||
"magic-string",
|
||||
"enquirer"
|
||||
"enquirer",
|
||||
"find-cache-dir"
|
||||
],
|
||||
"keepLifecycleScripts": true
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
+12
-1
@@ -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`,
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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 %}
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -14,4 +14,5 @@ export interface NodeExecutorOptions {
|
||||
port: number;
|
||||
watch?: boolean;
|
||||
debounce?: number;
|
||||
runBuildTargetDependencies?: boolean;
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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(
|
||||
{},
|
||||
|
||||
@@ -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,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> = {
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+29
-2
@@ -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": {
|
||||
|
||||
+21
-3
@@ -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": {
|
||||
|
||||
@@ -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'
|
||||
) {
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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 = [
|
||||
|
||||
@@ -36,7 +36,6 @@ const IGNORE_MATCHES_IN_PACKAGE = {
|
||||
'injection-js',
|
||||
'browserslist',
|
||||
'cacache',
|
||||
'find-cache-dir',
|
||||
'less',
|
||||
'node-sass',
|
||||
'node-sass-tilde-importer',
|
||||
|
||||
Reference in New Issue
Block a user