Compare commits

...

20 Commits

Author SHA1 Message Date
FrozenPandaz 29a7b8575e chore(misc): publish 16.8.1 2023-09-07 18:16:51 -04:00
Jason Jean 1f9dc40be2 fix(core): do not validate remote cache validity (#19059)
(cherry picked from commit f71f2ac1f0)
2023-09-07 16:44:08 -04:00
Jason Jean 21944222ca fix(js): handle nested wildcard imports and paths that start with # (#19056)
(cherry picked from commit 4b344ac660)
2023-09-07 16:10:31 -04:00
Jason Jean a7bc9d3406 chore(linter): fix e2e tests naively (#19058)
(cherry picked from commit bde5c731cd)
2023-09-07 16:10:13 -04:00
Jack Hsu 2e52c14cb8 fix(react): set "watch: false" on module federation serve-static options (#19052)
(cherry picked from commit 305b44310a)
2023-09-07 15:01:56 -04:00
Jason Jean 655a0a71cc fix(misc): use preset apps instead of empty (#19051)
(cherry picked from commit e8fb1f4f4b)
2023-09-07 15:01:54 -04:00
Craigory Coppola ac2bea7d87 fix(core): prettier 3 shouldn't cause errors due to esm + compile cache (#19042)
(cherry picked from commit f1be92e3df)
2023-09-07 15:01:51 -04:00
Jason Jean 18acd9afec fix(core): do not prompt, only warn when projectNameAndRootLayout is … (#19037)
(cherry picked from commit 850cdb3d20)
2023-09-07 15:01:48 -04:00
Jack Hsu 6ac6473129 fix(node): explicitly check that project is a library before updating imports (#19040)
(cherry picked from commit 214b53134d)
2023-09-07 15:01:44 -04:00
Miroslav Jonaš 58b13a26af fix(angular): keep dependency-checks enabled for buildable libraries (#19047)
(cherry picked from commit bcb5965ec5)
2023-09-07 15:01:36 -04:00
Katerina Skroumpelou 143b2230b6 fix(vite): check for undefined and create types array (#19045)
(cherry picked from commit f487929a9e)
2023-09-07 15:01:24 -04:00
Miroslav Jonaš 85b03c2bc4 fix(linter): ensure config manipulations are run only if config is supported (#19035)
(cherry picked from commit aa223621f7)
2023-09-07 15:01:22 -04:00
Jason Jean 2e0b09afc5 fix(misc): nx view-logs should open the nx-cloud link when connected … (#17808)
(cherry picked from commit 4940b2b0b2)
2023-09-07 15:01:05 -04:00
Adam Wootton 14a4ef4e93 fix(js): workspace lib devDependencies should not be added to package.json (#17802)
(cherry picked from commit 9ba98f4b25)
2023-09-07 15:00:52 -04:00
Craigory Coppola 22dadbc9b4 fix(core): register ts transpiler when running .ts backed plugins (#19027)
(cherry picked from commit 2526967fc5)
2023-09-07 15:00:48 -04:00
Matt Lewis 1f10398934 fix(webpack): enable in memory caching when building for node in watch mode (#18348)
(cherry picked from commit f30174b677)
2023-09-07 15:00:46 -04:00
Nadav Shatz 62f526aa0a fix(react): fix createGlobPAtternsForDependencies path (#18546)
(cherry picked from commit ac85a16a59)
2023-09-07 15:00:43 -04:00
Leosvel Pérez Espinosa 24de089b74 fix(misc): calculate cwd relative path correctly for generators and executors (#18933)
(cherry picked from commit ace8f8cf97)
2023-09-07 15:00:39 -04:00
Jack Hsu 4365c010e6 fix(linter): handle non-JSON eslintrc files when updating overrides (#19026)
(cherry picked from commit 8c1f183659)
2023-09-07 15:00:30 -04:00
Miroslav Jonaš 2e1d48e184 fix(linter): fix dep-checks projPackageJsonDeps caching for IDE (#18935)
(cherry picked from commit 2bc7031017)
2023-09-07 15:00:28 -04:00
36 changed files with 407 additions and 326 deletions
@@ -70,7 +70,7 @@ Now, let's continue by creating an empty Nx workspace.
```shell
# Replace acme with desired scope
npx create-nx-workspace acme --preset=empty
npx create-nx-workspace acme --preset=apps
cd acme
```
+1 -3
View File
@@ -706,7 +706,6 @@ describe('Linter', () => {
// should have plugin extends
expect(appEslint.overrides[1].extends).toBeDefined();
expect(appEslint.overrides[2].extends).toBeDefined();
expect(e2eEslint.overrides[0].extends).toBeDefined();
runCLI(`generate @nx/js:lib ${mylib} --no-interactive`);
@@ -717,8 +716,7 @@ describe('Linter', () => {
// should have no plugin extends
expect(appEslint.overrides[1].extends).toEqual([
'plugin:@nx/angular',
'plugin:@angular-eslint/template/process-inline-templates',
'plugin:@nx/angular-template',
]);
expect(e2eEslint.overrides[0].extends).toBeUndefined();
});
+1 -1
View File
@@ -142,7 +142,7 @@ describe('Node Applications', () => {
}
).toString();
expect(additionalResult).toContain('Hello Additional World!');
}, 60000);
}, 300_000);
it('should be able to generate an empty application with variable in .env file', async () => {
const originalEnvPort = process.env.PORT;
+1 -1
View File
@@ -1,6 +1,6 @@
{
"packages": ["build/packages/*", "build/packages/nx/native-packages/*"],
"version": "16.8.0",
"version": "16.8.1",
"granularPathspec": false,
"command": {
"publish": {
@@ -9,13 +9,6 @@ exports[`addLinting generator should correctly generate the .eslintrc.json file
"!**/*",
],
"overrides": [
{
"files": [
"*.json",
],
"parser": "jsonc-eslint-parser",
"rules": {},
},
{
"extends": [
"plugin:@nx/angular",
@@ -65,13 +58,6 @@ exports[`addLinting generator support angular v14 should correctly generate the
"!**/*",
],
"overrides": [
{
"files": [
"*.json",
],
"parser": "jsonc-eslint-parser",
"rules": {},
},
{
"extends": [
"plugin:@nx/angular",
@@ -2,6 +2,7 @@ import {
formatFiles,
GeneratorCallback,
joinPathFragments,
readProjectConfiguration,
runTasksInSerial,
Tree,
} from '@nx/devkit';
@@ -47,11 +48,6 @@ export async function addLintingGenerator(
.includes(`${options.projectRoot}/tsconfig.*?.json`);
replaceOverridesInLintConfig(tree, options.projectRoot, [
{
files: ['*.json'],
parser: 'jsonc-eslint-parser',
rules: {},
},
{
files: ['*.ts'],
...(hasParserOptions
@@ -93,6 +89,17 @@ export async function addLintingGenerator(
*/
rules: {},
},
...(isBuildableLibraryProject(tree, options.projectName)
? [
{
files: ['*.json'],
parser: 'jsonc-eslint-parser',
rules: {
'@nx/dependency-checks': 'error',
} as any,
},
]
: []),
]);
}
@@ -108,4 +115,13 @@ export async function addLintingGenerator(
return runTasksInSerial(...tasks);
}
function isBuildableLibraryProject(tree: Tree, projectName: string): boolean {
const projectConfig = readProjectConfiguration(tree, projectName);
return (
projectConfig.projectType === 'library' &&
projectConfig.targets?.build &&
!!projectConfig.targets.build
);
}
export default addLintingGenerator;
@@ -512,13 +512,6 @@ describe('app', () => {
"!**/*",
],
"overrides": [
{
"files": [
"*.json",
],
"parser": "jsonc-eslint-parser",
"rules": {},
},
{
"extends": [
"plugin:@nx/angular",
@@ -253,13 +253,6 @@ exports[`convert-tslint-to-eslint should not override .eslint config if migratio
"!**/*",
],
"overrides": [
{
"files": [
"*.json",
],
"parser": "jsonc-eslint-parser",
"rules": {},
},
{
"extends": [
"plugin:@nx/angular",
@@ -851,13 +844,6 @@ exports[`convert-tslint-to-eslint should work for Angular applications 4`] = `
"!**/*",
],
"overrides": [
{
"files": [
"*.json",
],
"parser": "jsonc-eslint-parser",
"rules": {},
},
{
"extends": [
"plugin:@nx/angular",
@@ -1211,13 +1197,6 @@ exports[`convert-tslint-to-eslint should work for Angular libraries 4`] = `
"!**/*",
],
"overrides": [
{
"files": [
"*.json",
],
"parser": "jsonc-eslint-parser",
"rules": {},
},
{
"extends": [
"plugin:@nx/angular",
@@ -597,11 +597,6 @@ describe('lib', () => {
"extends": ["../../.eslintrc.json"],
"ignorePatterns": ["!**/*"],
"overrides": [
{
"files": ["*.json"],
"parser": "jsonc-eslint-parser",
"rules": {}
},
{
"files": ["*.ts"],
"extends": [
@@ -631,6 +626,13 @@ describe('lib', () => {
"files": ["*.html"],
"extends": ["plugin:@nx/angular-template"],
"rules": {}
},
{
"files": ["*.json"],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/dependency-checks": "error"
}
}
]
}
@@ -1158,12 +1160,65 @@ describe('lib', () => {
],
"overrides": [
{
"files": [
"*.json",
"extends": [
"plugin:@nx/angular",
"plugin:@angular-eslint/template/process-inline-templates",
],
"files": [
"*.ts",
],
"rules": {
"@angular-eslint/component-selector": [
"error",
{
"prefix": "proj",
"style": "kebab-case",
"type": "element",
},
],
"@angular-eslint/directive-selector": [
"error",
{
"prefix": "proj",
"style": "camelCase",
"type": "attribute",
},
],
},
},
{
"extends": [
"plugin:@nx/angular-template",
],
"files": [
"*.html",
],
"parser": "jsonc-eslint-parser",
"rules": {},
},
],
}
`);
});
it('should add dependency checks to buildable libs', async () => {
// ACT
await runLibraryGeneratorWithOpts({
linter: Linter.EsLint,
buildable: true,
});
// ASSERT
const eslintConfig = readJson(tree, 'my-lib/.eslintrc.json');
expect(eslintConfig).toMatchInlineSnapshot(`
{
"extends": [
"../.eslintrc.json",
],
"ignorePatterns": [
"!**/*",
],
"overrides": [
{
"extends": [
"plugin:@nx/angular",
@@ -1200,6 +1255,15 @@ describe('lib', () => {
],
"rules": {},
},
{
"files": [
"*.json",
],
"parser": "jsonc-eslint-parser",
"rules": {
"@nx/dependency-checks": "error",
},
},
],
}
`);
@@ -57,6 +57,6 @@ export function readTargetOptions<T = any>(
targetConfiguration,
schema,
defaultProject,
relative(context.cwd, context.root)
relative(context.root, context.cwd)
) as T;
}
@@ -63,6 +63,10 @@ type ProjectNameAndRootFormats = {
derived?: ProjectNameAndRootOptions;
};
const deprecationWarning = stripIndents`
In Nx 18, generating projects will no longer derive the name and root.
Please provide the exact project name and root in the future.`;
export async function determineProjectNameAndRootOptions(
tree: Tree,
options: ProjectGenerationOptions
@@ -73,11 +77,18 @@ export async function determineProjectNameAndRootOptions(
> {
validateName(options.name, options.projectNameAndRootFormat);
const formats = getProjectNameAndRootFormats(tree, options);
const configuredDefault = getDefaultProjectNameAndRootFormat(tree);
if (configuredDefault === 'derived') {
logger.warn(
deprecationWarning + '\n' + getExample(options.callingGenerator, formats)
);
}
const format =
options.projectNameAndRootFormat ??
(getDefaultProjectNameAndRootFormat(tree) === 'as-provided'
? 'as-provided'
: await determineFormat(tree, formats, options.callingGenerator));
configuredDefault ??
(await determineFormat(tree, formats, options.callingGenerator));
return {
...formats[format],
@@ -115,6 +126,13 @@ function validateName(
}
}
function getExample(
callingGenerator: string,
formats: ProjectNameAndRootFormats
) {
return `Example: nx g ${callingGenerator} ${formats['as-provided'].projectName} --directory ${formats['as-provided'].projectRoot}`;
}
async function determineFormat(
tree: Tree,
formats: ProjectNameAndRootFormats,
@@ -157,10 +175,6 @@ async function determineFormat(
format === asProvidedSelectedValue ? 'as-provided' : 'derived'
);
const deprecationWarning = stripIndents`
In Nx 18, generating projects will no longer derive the name and root.
Please provide the exact project name and root in the future.`;
if (result === 'as-provided' && callingGenerator) {
const { saveDefault } = await prompt<{ saveDefault: boolean }>({
type: 'confirm',
@@ -174,7 +188,7 @@ async function determineFormat(
logger.warn(deprecationWarning);
}
} else {
const example = `Example: nx g ${callingGenerator} ${formats[result].projectName} --directory ${formats[result].projectRoot}`;
const example = getExample(callingGenerator, formats);
logger.warn(deprecationWarning + '\n' + example);
}
@@ -190,7 +204,7 @@ function setProjectNameAndRootFormatDefault(tree: Tree) {
function getDefaultProjectNameAndRootFormat(tree: Tree) {
const nxJson = readNxJson(tree);
return nxJson.workspaceLayout?.projectNameAndRootFormat ?? 'derived';
return nxJson.workspaceLayout?.projectNameAndRootFormat;
}
function getProjectNameAndRootFormats(
@@ -148,11 +148,8 @@ export default createESLintRule<Options, MessageIds>({
'package.json'
);
globalThis.projPackageJsonDeps ??= getProductionDependencies(
getPackageJson(projPackageJsonPath)
);
const projPackageJsonDeps: Record<string, string> =
globalThis.projPackageJsonDeps;
getProductionDependencies(projPackageJsonPath);
const rootPackageJsonDeps = getAllDependencies(rootPackageJson);
function validateMissingDependencies(node: AST.JSONProperty) {
@@ -1,6 +1,7 @@
import { ProjectFileMap, readJsonFile } from '@nx/devkit';
import { readJsonFile } from '@nx/devkit';
import { existsSync } from 'fs';
import { PackageJson } from 'nx/src/utils/package-json';
import { isTerminalRun } from './runtime-lint-utils';
export function getAllDependencies(
packageJson: PackageJson
@@ -14,13 +15,18 @@ export function getAllDependencies(
}
export function getProductionDependencies(
packageJson: PackageJson
packageJsonPath: string
): Record<string, string> {
return {
...packageJson.dependencies,
...packageJson.peerDependencies,
...packageJson.optionalDependencies,
};
if (!globalThis.projPackageJsonDeps || !isTerminalRun()) {
const packageJson = getPackageJson(packageJsonPath);
globalThis.projPackageJsonDeps = {
...packageJson.dependencies,
...packageJson.peerDependencies,
...packageJson.optionalDependencies,
};
}
return globalThis.projPackageJsonDeps;
}
export function getPackageJson(path: string): PackageJson {
@@ -19,13 +19,13 @@ export function ensureGlobalProjectGraph(ruleName: string) {
* Enforce every IDE change to get a fresh nxdeps.json
*/
if (
!(global as any).projectGraph ||
!(global as any).projectRootMappings ||
!(global as any).projectFileMap ||
!globalThis.projectGraph ||
!globalThis.projectRootMappings ||
!globalThis.projectFileMap ||
!isTerminalRun()
) {
const nxJson = readNxJson();
(global as any).workspaceLayout = nxJson.workspaceLayout;
globalThis.workspaceLayout = nxJson.workspaceLayout;
/**
* Because there are a number of ways in which the rule can be invoked (executor vs ESLint CLI vs IDE Plugin),
@@ -33,12 +33,12 @@ export function ensureGlobalProjectGraph(ruleName: string) {
*/
try {
const projectGraph = readCachedProjectGraph();
(global as any).projectGraph = projectGraph;
(global as any).projectRootMappings = createProjectRootMappings(
globalThis.projectGraph = projectGraph;
globalThis.projectRootMappings = createProjectRootMappings(
projectGraph.nodes
);
(global as any).projectFileMap = readProjectFileMapCache().projectFileMap;
(global as any).targetProjectLocator = new TargetProjectLocator(
globalThis.projectFileMap = readProjectFileMapCache().projectFileMap;
globalThis.targetProjectLocator = new TargetProjectLocator(
projectGraph.nodes,
projectGraph.externalNodes
);
@@ -61,9 +61,9 @@ export function readProjectGraph(ruleName: string): {
} {
ensureGlobalProjectGraph(ruleName);
return {
projectGraph: (global as any).projectGraph,
projectFileMap: (global as any).projectFileMap,
projectRootMappings: (global as any).projectRootMappings,
targetProjectLocator: (global as any).targetProjectLocator,
projectGraph: globalThis.projectGraph,
projectFileMap: globalThis.projectFileMap,
projectRootMappings: globalThis.projectRootMappings,
targetProjectLocator: globalThis.targetProjectLocator,
};
}
+12 -9
View File
@@ -267,18 +267,21 @@ export async function addLint(
// nx-ignore-next-line
} = require('@nx/linter/src/generators/utils/eslint-file');
// if config is not supported, we don't need to do anything
if (!isEslintConfigSupported(tree)) {
return task;
}
// Also update the root ESLint config. The lintProjectGenerator will not generate it for root projects.
// But we need to set the package.json checks.
if (options.rootProject) {
if (isEslintConfigSupported(tree)) {
addOverrideToLintConfig(tree, '', {
files: ['*.json'],
parser: 'jsonc-eslint-parser',
rules: {
'@nx/dependency-checks': 'error',
},
});
}
addOverrideToLintConfig(tree, '', {
files: ['*.json'],
parser: 'jsonc-eslint-parser',
rules: {
'@nx/dependency-checks': 'error',
},
});
}
// If project lints package.json with @nx/dependency-checks, then add ignore files for
@@ -144,8 +144,13 @@ function addMissingDependencies(
packageJson[propType][packageName] = version;
} else {
const packageName = entry.name;
if (!!workspacePackageJson.devDependencies?.[packageName]) {
return;
}
if (
!packageJson.dependencies?.[packageName] &&
!packageJson.devDependencies?.[packageName] &&
!packageJson.peerDependencies?.[packageName]
) {
const outputs = getOutputsForTargetAndConfiguration(
@@ -2,6 +2,7 @@ import {
baseEsLintConfigFile,
eslintConfigFileWhitelist,
findEslintFile,
lintConfigHasOverride,
} from './eslint-file';
import { Tree } from '@nx/devkit';
@@ -36,4 +37,40 @@ describe('@nx/linter:eslint-file', () => {
}
);
});
describe('lintConfigHasOverride', () => {
it('should return true when override exists in eslintrc format', () => {
tree.write(
'.eslintrc.json',
'{"overrides": [{ "files": ["*.ts"], "rules": {} }]}'
);
expect(
lintConfigHasOverride(
tree,
'.',
(o) => {
return o.files?.includes('*.ts');
},
false
)
).toBe(true);
});
it('should return false when eslintrc is not in JSON format', () => {
tree.write(
'.eslintrc.js',
'module.exports = {overrides: [{ files: ["*.ts"], rules: {} }]};'
);
expect(
lintConfigHasOverride(
tree,
'.',
(o) => {
return o.files?.includes('*.ts');
},
false
)
).toBe(false);
});
});
});
@@ -55,7 +55,7 @@ export function findEslintFile(tree: Tree, projectRoot = ''): string | null {
export function isEslintConfigSupported(tree: Tree, projectRoot = ''): boolean {
const eslintFile = findEslintFile(tree, projectRoot);
if (!eslintFile) {
return;
return false;
}
return eslintFile.endsWith('.json') || eslintFile.endsWith('.config.js');
}
@@ -233,6 +233,9 @@ export function lintConfigHasOverride(
lookup: (override: Linter.ConfigOverride<Linter.RulesRecord>) => boolean,
checkBaseConfig = false
): boolean {
if (!isEslintConfigSupported(tree, root)) {
return false;
}
const isBase =
checkBaseConfig && findEslintFile(tree, root).includes('.base');
if (useFlatConfig(tree)) {
@@ -247,6 +250,7 @@ export function lintConfigHasOverride(
root,
isBase ? baseEsLintConfigFile : '.eslintrc.json'
);
return readJson(tree, fileName).overrides?.some(lookup) || false;
}
}
@@ -8,6 +8,7 @@ describe('update-16-8-0-add-ignored-files migration', () => {
beforeEach(() => {
tree = createTreeWithEmptyWorkspace();
tree.write('.eslintrc.json', '{}');
});
it('should run successfully when eslint config is not present', async () => {
@@ -2,6 +2,7 @@ import { getProjects, Tree } from '@nx/devkit';
import { forEachExecutorOptions } from '@nx/devkit/src/generators/executor-options-utils';
import {
findEslintFile,
isEslintConfigSupported,
lintConfigHasOverride,
updateOverrideInLintConfig,
} from '../../generators/utils/eslint-file';
@@ -16,7 +17,12 @@ export default function update(tree: Tree) {
const addIgnorePattern =
(ignorePattern: string) => (_options: unknown, projectName: string) => {
const project = projects.get(projectName);
if (!findEslintFile(tree, project.root)) return;
if (
!findEslintFile(tree, project.root) ||
!isEslintConfigSupported(tree)
) {
return;
}
if (
lintConfigHasOverride(
tree,
@@ -45,6 +45,7 @@ export async function e2eProjectGeneratorInternal(
addProjectConfiguration(host, options.e2eProjectName, {
root: options.e2eProjectRoot,
implicitDependencies: [options.project],
projectType: 'application',
targets: {
e2e: {
executor: '@nx/jest:jest',
+11 -1
View File
@@ -51,7 +51,17 @@ function main() {
process.env.NX_DAEMON = 'false';
require('nx/src/command-line/nx-commands').commandsObject.argv;
} else {
if (workspace && workspace.type === 'nx') {
// v8-compile-cache doesn't support ESM. Attempting to import ESM
// with it enabled results in an error that reads "Invalid host options".
//
// Angular CLI, and prettier both use ESM so we need to disable it in these cases.
if (
workspace &&
workspace.type === 'nx' &&
!['format', 'format:check', 'format:write', 'g', 'generate'].some(
(cmd) => process.argv[3] === cmd
)
) {
require('v8-compile-cache');
}
// polyfill rxjs observable to avoid issues with multiple version of Observable installed in node_modules
@@ -5,60 +5,68 @@ import { output } from '../../utils/output';
import { runNxSync } from '../../utils/child-process';
export async function viewLogs(): Promise<number> {
const cloudUsed = isNxCloudUsed();
if (cloudUsed) {
output.error({
title: 'Your workspace is already connected to Nx Cloud',
bodyLines: [
`Refer to the output of the last command to find the Nx Cloud link to view the run details.`,
],
});
return 1;
}
const installCloud = await (
await import('enquirer')
)
.prompt([
{
name: 'NxCloud',
message: `To view the logs, Nx needs to connect your workspace to Nx Cloud and upload the most recent run details.`,
type: 'autocomplete',
choices: [
{
name: 'Yes',
hint: 'Connect to Nx Cloud and upload the run details',
},
{
name: 'No',
},
],
initial: 'Yes' as any,
},
])
.then((a: { NxCloud: 'Yes' | 'No' }) => a.NxCloud === 'Yes');
if (!installCloud) return;
const pmc = getPackageManagerCommand();
const cloudUsed = isNxCloudUsed() && false;
if (!cloudUsed) {
const installCloud = await (
await import('enquirer')
)
.prompt([
{
name: 'NxCloud',
message: `To view the logs, Nx needs to connect your workspace to Nx Cloud and upload the most recent run details.`,
type: 'autocomplete',
choices: [
{
name: 'Yes',
hint: 'Connect to Nx Cloud and upload the run details',
},
{
name: 'No',
},
],
initial: 'Yes' as any,
},
])
.then((a: { NxCloud: 'Yes' | 'No' }) => a.NxCloud === 'Yes');
try {
output.log({
title: 'Installing nx-cloud',
});
execSync(`${pmc.addDev} nx-cloud@latest`, { stdio: 'ignore' });
} catch (e) {
output.log({
title: 'Installation failed',
});
console.log(e);
return 1;
}
if (!installCloud) return;
try {
output.log({
title: 'Installing nx-cloud',
});
execSync(`${pmc.addDev} nx-cloud@latest`, { stdio: 'ignore' });
} catch (e) {
output.log({
title: 'Installation failed',
});
console.log(e);
return 1;
}
try {
output.log({
title: 'Connecting to Nx Cloud',
});
runNxSync(`g nx-cloud:init --installation-source=view-logs`, {
stdio: 'ignore',
});
} catch (e) {
output.log({
title: 'Failed to connect to Nx Cloud',
});
console.log(e);
return 1;
}
try {
output.log({
title: 'Connecting to Nx Cloud',
});
runNxSync(`g nx-cloud:init --installation-source=view-logs`, {
stdio: 'ignore',
});
} catch (e) {
output.log({
title: 'Failed to connect to Nx Cloud',
});
console.log(e);
return 1;
}
execSync(`${pmc.exec} nx-cloud upload-and-show-run-details`, {
@@ -364,7 +364,7 @@ export async function generate(cwd: string, args: { [k: string]: any }) {
projectsConfigurations,
nxJsonConfiguration
),
relative(cwd, workspaceRoot),
relative(workspaceRoot, cwd),
verbose
);
+1 -1
View File
@@ -150,7 +150,7 @@ async function runExecutorInternal<T extends { success: boolean }>(
targetConfig,
schema,
project,
relative(cwd, root),
relative(root, cwd),
isVerbose
);
@@ -1,4 +1,8 @@
import { vol } from 'memfs';
jest.mock('../../../../utils/workspace-root', () => ({
workspaceRoot: '/root',
}));
jest.mock('fs', () => require('memfs').fs);
import { TargetProjectLocator } from './target-project-locator';
import {
ProjectGraphExternalNode,
@@ -6,22 +10,12 @@ import {
ProjectGraphProjectNode,
} from '../../../../config/project-graph';
jest.mock('nx/src/utils/workspace-root', () => ({
workspaceRoot: '/root',
}));
jest.mock('fs', () => require('memfs').fs);
describe('findTargetProjectWithImport', () => {
let projects: Record<string, ProjectGraphProjectNode>;
let npmProjects: Record<string, ProjectGraphExternalNode>;
let fsJson;
let targetProjectLocator: TargetProjectLocator;
beforeEach(() => {
const projecstConfigurations = {
projects: {
proj1: {},
},
};
const nxJson = {
npmScope: 'proj',
};
@@ -44,109 +38,16 @@ describe('findTargetProjectWithImport', () => {
'@proj/proj1234/*': ['libs/proj1234/*'],
'@proj/proj1234-child': ['libs/proj1234-child'],
'@proj/proj1234-child/*': ['libs/proj1234-child/*'],
'#hash-path': ['libs/hash-project/src/index.ts'],
'parent-path/*': ['libs/parent-path/*'],
},
},
};
fsJson = {
'./workspace.json': JSON.stringify(projecstConfigurations),
'./nx.json': JSON.stringify(nxJson),
'./tsconfig.base.json': JSON.stringify(tsConfig),
'./libs/proj/index.ts': `import {a} from '@proj/my-second-proj';
import('@proj/project-3');
const a = { loadChildren: '@proj/proj4ab#a' };
`,
'./libs/proj2/index.ts': `export const a = 2;`,
'./libs/proj2/deep/index.ts': `export const a = 22;`,
'./libs/proj3a/index.ts': `export const a = 3;`,
'./libs/proj4ab/index.ts': `export const a = 4;`,
'./libs/proj5/index.ts': `export const a = 5;`,
'./libs/proj6/index.ts': `export const a = 6;`,
'./libs/proj7/index.ts': `export const a = 7;`,
'./libs/proj123/index.ts': 'export const a = 123',
'./libs/proj1234/index.ts': 'export const a = 1234',
'./libs/proj1234-child/index.ts': 'export const a = 12345',
};
vol.fromJSON(fsJson, '/root');
const ctx = {
workspace: {
...projecstConfigurations,
...nxJson,
} as any,
fileMap: {
rootProj: [
{
file: 'index.ts',
hash: 'some-hash',
},
],
proj: [
{
file: 'libs/proj/index.ts',
hash: 'some-hash',
},
],
proj2: [
{
file: 'libs/proj2/index.ts',
hash: 'some-hash',
},
{
file: 'libs/proj2/deep/index.ts',
hash: 'some-hash',
},
],
proj3a: [
{
file: 'libs/proj3a/index.ts',
hash: 'some-hash',
},
],
proj4ab: [
{
file: 'libs/proj4ab/index.ts',
hash: 'some-hash',
},
],
proj5: [
{
file: 'libs/proj5/index.ts',
hash: 'some-hash',
},
],
proj6: [
{
file: 'libs/proj6/index.ts',
hash: 'some-hash',
},
],
proj7: [
{
file: 'libs/proj7/index.ts',
hash: 'some-hash',
},
],
proj123: [
{
file: 'libs/proj123/index.ts',
hash: 'some-hash',
},
],
proj1234: [
{
file: 'libs/proj1234/index.ts',
hash: 'some-hash',
},
],
'proj1234-child': [
{
file: 'libs/proj1234-child/index.ts',
hash: 'some-hash',
},
],
},
} as any;
projects = {
rootProj: {
name: 'rootProj',
@@ -225,6 +126,27 @@ describe('findTargetProjectWithImport', () => {
root: 'libs/proj1234-child',
},
},
'hash-project': {
name: 'hash-project',
type: 'lib',
data: {
root: 'libs/hash-project',
},
},
'parent-project': {
name: 'parent-project',
type: 'lib',
data: {
root: 'libs/parent-path',
},
},
'child-project': {
name: 'child-project',
type: 'lib',
data: {
root: 'libs/parent-path/child-path',
},
},
};
npmProjects = {
'npm:@ng/core': {
@@ -380,7 +302,7 @@ describe('findTargetProjectWithImport', () => {
expect(parentProj).toEqual('proj1234');
});
it('should be able to npm dependencies', () => {
it('should be able to locate npm dependencies', () => {
const result1 = targetProjectLocator.findProjectWithImport(
'@ng/core',
'libs/proj1/index.ts'
@@ -394,14 +316,31 @@ describe('findTargetProjectWithImport', () => {
expect(result2).toEqual('npm:npm-package');
});
it('should be able to resolve a module using a normalized path', () => {
const proj4ab = targetProjectLocator.findProjectWithImport(
'@proj/proj4ab#a',
it('should be able to resolve wildcard paths', () => {
const parentProject = targetProjectLocator.findProjectWithImport(
'parent-path',
'libs/proj1/index.ts'
);
expect(proj4ab).toEqual('proj4ab');
expect(parentProject).toEqual('parent-project');
const childProject = targetProjectLocator.findProjectWithImport(
'parent-path/child-path',
'libs/proj1/index.ts'
);
expect(childProject).toEqual('child-project');
});
it('should be able to resolve paths that start with a #', () => {
const proj = targetProjectLocator.findProjectWithImport(
'#hash-path',
'libs/proj1/index.ts'
);
expect(proj).toEqual('hash-project');
});
it('should be able to resolve a modules when npm packages exist', () => {
const proj5 = targetProjectLocator.findProjectWithImport(
'@proj/proj5',
@@ -785,14 +724,6 @@ describe('findTargetProjectWithImport (without tsconfig.json)', () => {
expect(result2).toEqual('npm:npm-package');
});
it('should be able to resolve a module using a normalized path', () => {
const proj4ab = targetProjectLocator.findProjectWithImport(
'@proj/proj4ab#a',
'libs/proj1/index.ts'
);
expect(proj4ab).toEqual('proj4ab');
});
it('should be able to resolve paths that have similar names', () => {
const proj = targetProjectLocator.findProjectWithImport(
'@proj/proj123',
@@ -3,7 +3,7 @@ import {
resolveModuleByImport,
} from '../../utils/typescript';
import { isRelativePath, readJsonFile } from '../../../../utils/fileutils';
import { dirname, join, posix } from 'path';
import { dirname, join, posix, relative, resolve } from 'path';
import { workspaceRoot } from '../../../../utils/workspace-root';
import {
ProjectGraphExternalNode,
@@ -40,33 +40,33 @@ export class TargetProjectLocator {
* @param filePath
*/
findProjectWithImport(importExpr: string, filePath: string): string {
const normalizedImportExpr = importExpr.split('#')[0];
if (isRelativePath(normalizedImportExpr)) {
const resolvedModule = posix.join(
dirname(filePath),
normalizedImportExpr
);
if (isRelativePath(importExpr)) {
const resolvedModule = posix.join(dirname(filePath), importExpr);
return this.findProjectOfResolvedModule(resolvedModule);
}
// find project using tsconfig paths
const paths = this.findPaths(normalizedImportExpr);
if (paths) {
const results = this.findPaths(importExpr);
if (results) {
const [path, paths] = results;
for (let p of paths) {
const maybeResolvedProject = this.findProjectOfResolvedModule(p);
const r = p.endsWith('/*')
? join(dirname(p), relative(path.replace(/\*$/, ''), importExpr))
: p;
const maybeResolvedProject = this.findProjectOfResolvedModule(r);
if (maybeResolvedProject) {
return maybeResolvedProject;
}
}
}
if (builtInModuleSet.has(normalizedImportExpr)) {
this.npmResolutionCache.set(normalizedImportExpr, null);
if (builtInModuleSet.has(importExpr)) {
this.npmResolutionCache.set(importExpr, null);
return null;
}
// try to find npm package before using expensive typescript resolution
const npmProject = this.findNpmPackage(normalizedImportExpr);
const npmProject = this.findNpmPackage(importExpr);
if (npmProject) {
return npmProject;
}
@@ -76,7 +76,7 @@ export class TargetProjectLocator {
// and existed only because of the incomplete `paths` matching
// if import cannot be matched using tsconfig `paths` the compilation would fail anyway
const resolvedProject = this.resolveImportWithTypescript(
normalizedImportExpr,
importExpr,
filePath
);
if (resolvedProject) {
@@ -86,7 +86,7 @@ export class TargetProjectLocator {
try {
const resolvedModule = this.resolveImportWithRequire(
normalizedImportExpr,
importExpr,
filePath
);
@@ -94,7 +94,7 @@ export class TargetProjectLocator {
} catch {}
// nothing found, cache for later
this.npmResolutionCache.set(normalizedImportExpr, null);
this.npmResolutionCache.set(importExpr, null);
return null;
}
@@ -108,7 +108,7 @@ export class TargetProjectLocator {
return undefined;
}
if (this.paths[normalizedImportExpr]) {
return this.paths[normalizedImportExpr];
return [normalizedImportExpr, this.paths[normalizedImportExpr]];
}
const wildcardPath = Object.keys(this.paths).find(
(path) =>
@@ -117,7 +117,7 @@ export class TargetProjectLocator {
normalizedImportExpr === path.replace(/\/\*$/, ''))
);
if (wildcardPath) {
return this.paths[wildcardPath];
return [wildcardPath, this.paths[wildcardPath]];
}
return undefined;
}
+26 -25
View File
@@ -65,6 +65,7 @@ export class Cache {
const res = await this.getFromLocalDir(task);
if (res) {
await this.assertLocalCacheValidity(task);
return { ...res, remote: false };
} else if (this.options.remoteCache) {
// didn't find it locally but we have a remote cache
@@ -227,31 +228,6 @@ export class Cache {
code = Number(await readFile(join(td, 'code'), 'utf-8'));
} catch {}
let sourceMachineId = null;
try {
sourceMachineId = await readFile(join(td, 'source'), 'utf-8');
} catch {}
if (
sourceMachineId &&
sourceMachineId != (await this.currentMachineId())
) {
if (
process.env.NX_REJECT_UNKNOWN_LOCAL_CACHE != '0' &&
process.env.NX_REJECT_UNKNOWN_LOCAL_CACHE != 'false'
) {
const error = [
`Invalid Cache Directory for Task "${task.id}"`,
`The local cache artifact in "${td}" was not been generated on this machine.`,
`As a result, the cache's content integrity cannot be confirmed, which may make cache restoration potentially unsafe.`,
`If your machine ID has changed since the artifact was cached, run "nx reset" to fix this issue.`,
`Read about the error and how to address it here: https://nx.dev/recipes/troubleshooting/unknown-local-cache`,
``,
].join('\n');
throw new Error(error);
}
}
return {
terminalOutput,
outputsPath: join(td, 'outputs'),
@@ -262,6 +238,31 @@ export class Cache {
}
}
private async assertLocalCacheValidity(task: Task) {
const td = join(this.cachePath, task.hash);
let sourceMachineId = null;
try {
sourceMachineId = await readFile(join(td, 'source'), 'utf-8');
} catch {}
if (sourceMachineId && sourceMachineId != (await this.currentMachineId())) {
if (
process.env.NX_REJECT_UNKNOWN_LOCAL_CACHE != '0' &&
process.env.NX_REJECT_UNKNOWN_LOCAL_CACHE != 'false'
) {
const error = [
`Invalid Cache Directory for Task "${task.id}"`,
`The local cache artifact in "${td}" was not been generated on this machine.`,
`As a result, the cache's content integrity cannot be confirmed, which may make cache restoration potentially unsafe.`,
`If your machine ID has changed since the artifact was cached, run "nx reset" to fix this issue.`,
`Read about the error and how to address it here: https://nx.dev/recipes/troubleshooting/unknown-local-cache`,
``,
].join('\n');
throw new Error(error);
}
}
}
private createCacheDir() {
mkdirSync(cacheDir, { recursive: true });
return cacheDir;
+9 -1
View File
@@ -172,8 +172,16 @@ function getPluginPathAndName(
}
const packageJsonPath = path.join(pluginPath, 'package.json');
const extension = path.extname(pluginPath);
// Register the ts-transpiler if we are pointing to a
// plain ts file that's not part of a plugin project
if (extension === '.ts' && !tsNodeAndPathsRegistered) {
registerPluginTSTranspiler();
}
const { name } =
!['.ts', '.js'].some((x) => x === path.extname(pluginPath)) && // Not trying to point to a ts or js file
!['.ts', '.js'].some((x) => x === extension) && // Not trying to point to a ts or js file
existsSync(packageJsonPath) // plugin has a package.json
? readJsonFile(packageJsonPath) // read name from package.json
: { name: moduleName };
@@ -53,7 +53,7 @@ function createTestProject() {
});
execSync(
`<%= packageManagerCommands.exec %> --yes create-nx-workspace@latest ${projectName} --preset empty --no-nxCloud --no-interactive`,
`<%= packageManagerCommands.exec %> --yes create-nx-workspace@latest ${projectName} --preset apps --no-nxCloud --no-interactive`,
{
cwd: dirname(projectDirectory),
stdio: 'inherit',
@@ -15,7 +15,7 @@ function runNxNewCommand(args?: string, silent?: boolean) {
return execSync(
`node ${require.resolve(
'nx'
)} new proj --nx-workspace-root=${localTmpDir} --no-interactive --skip-install --collection=@nx/workspace --npmScope=proj --preset=empty ${
)} new proj --nx-workspace-root=${localTmpDir} --no-interactive --skip-install --collection=@nx/workspace --npmScope=proj --preset=apps ${
args || ''
}`,
{
@@ -38,6 +38,7 @@ export function updateModuleFederationProject(
defaultConfiguration: 'production',
options: {
buildTarget: `${options.projectName}:build`,
watch: false,
port: options.devServerPort,
},
configurations: {
+1 -1
View File
@@ -7,7 +7,7 @@ import { createGlobPatternsForDependencies as jsGenerateGlobs } from '@nx/js/src
*/
export function createGlobPatternsForDependencies(
dirPath: string,
fileGlobPattern: string = '/**/!(*.stories|*.spec).{tsx,ts,jsx,js,html}'
fileGlobPattern: string = '/**/*!(*.stories|*.spec).{tsx,ts,jsx,js,html}'
) {
try {
return jsGenerateGlobs(dirPath, fileGlobPattern);
@@ -179,6 +179,12 @@ export async function viteConfigurationGenerator(
if (projectType === 'library') {
// update tsconfig.lib.json to include vite/client
updateJson(tree, joinPathFragments(root, 'tsconfig.lib.json'), (json) => {
if (!json.compilerOptions) {
json.compilerOptions = {};
}
if (!json.compilerOptions.types) {
json.compilerOptions.types = [];
}
if (!json.compilerOptions.types.includes('vite/client')) {
return {
...json,
+6
View File
@@ -191,6 +191,12 @@ export function withNx(pluginOptions?: WithNxOptions): NxWebpackPlugin {
process.env.NODE_ENV === 'production'
? (process.env.NODE_ENV as 'development' | 'production')
: ('none' as const),
// When target is Node, the Webpack mode will be set to 'none' which disables in memory caching and causes a full rebuild on every change.
// So to mitigate this we enable in memory caching when target is Node and in watch mode.
cache:
options.target === ('node' as const) && options.watch
? { type: 'memory' as const }
: undefined,
devtool:
options.sourceMap === 'hidden'
? 'hidden-source-map'
@@ -34,7 +34,7 @@ export function updateImports(
schema: NormalizedSchema,
project: ProjectConfiguration
) {
if (project.projectType === 'application') {
if (project.projectType !== 'library') {
return;
}