fix(misc): ensure plugins are not creating workspace context while creating nodes (#26253)
This commit is contained in:
parent
a308e1dc6b
commit
6f223005b8
@ -129,6 +129,7 @@ It only uses language primitives and immutable objects
|
||||
- [getProjects](../../devkit/documents/getProjects)
|
||||
- [getWorkspaceLayout](../../devkit/documents/getWorkspaceLayout)
|
||||
- [glob](../../devkit/documents/glob)
|
||||
- [globAsync](../../devkit/documents/globAsync)
|
||||
- [hashArray](../../devkit/documents/hashArray)
|
||||
- [installPackagesTask](../../devkit/documents/installPackagesTask)
|
||||
- [isWorkspacesEnabled](../../devkit/documents/isWorkspacesEnabled)
|
||||
|
||||
@ -18,3 +18,7 @@ Paths should be unix-style with forward slashes.
|
||||
`string`[]
|
||||
|
||||
Normalized paths in the workspace that match the provided glob patterns.
|
||||
|
||||
**`Deprecated`**
|
||||
|
||||
Use [globAsync](../../devkit/documents/globAsync) instead.
|
||||
|
||||
20
docs/generated/devkit/globAsync.md
Normal file
20
docs/generated/devkit/globAsync.md
Normal file
@ -0,0 +1,20 @@
|
||||
# Function: globAsync
|
||||
|
||||
▸ **globAsync**(`tree`, `patterns`): `Promise`\<`string`[]\>
|
||||
|
||||
Performs a tree-aware glob search on the files in a workspace. Able to find newly
|
||||
created files and hides deleted files before the updates are committed to disk.
|
||||
Paths should be unix-style with forward slashes.
|
||||
|
||||
#### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
| :--------- | :------------------------------------ | :---------------------- |
|
||||
| `tree` | [`Tree`](../../devkit/documents/Tree) | The file system tree |
|
||||
| `patterns` | `string`[] | A list of glob patterns |
|
||||
|
||||
#### Returns
|
||||
|
||||
`Promise`\<`string`[]\>
|
||||
|
||||
Normalized paths in the workspace that match the provided glob patterns.
|
||||
@ -129,6 +129,7 @@ It only uses language primitives and immutable objects
|
||||
- [getProjects](../../devkit/documents/getProjects)
|
||||
- [getWorkspaceLayout](../../devkit/documents/getWorkspaceLayout)
|
||||
- [glob](../../devkit/documents/glob)
|
||||
- [globAsync](../../devkit/documents/globAsync)
|
||||
- [hashArray](../../devkit/documents/hashArray)
|
||||
- [installPackagesTask](../../devkit/documents/installPackagesTask)
|
||||
- [isWorkspacesEnabled](../../devkit/documents/isWorkspacesEnabled)
|
||||
|
||||
@ -12,4 +12,5 @@ module.exports = {
|
||||
coverageReporters: ['html'],
|
||||
maxWorkers: 1,
|
||||
testEnvironment: 'node',
|
||||
setupFiles: ['../../scripts/unit-test-setup.js'],
|
||||
};
|
||||
|
||||
@ -75,6 +75,11 @@ describe('Cypress builder', () => {
|
||||
configuration,
|
||||
};
|
||||
};
|
||||
(devkit as any).logger = {
|
||||
warn: jest.fn(),
|
||||
log: jest.fn(),
|
||||
info: jest.fn(),
|
||||
};
|
||||
cypressRun = jest
|
||||
.spyOn(Cypress, 'run')
|
||||
.mockReturnValue(Promise.resolve({}));
|
||||
|
||||
@ -19,12 +19,13 @@ import { getLockFileName } from '@nx/js';
|
||||
|
||||
import { getNamedInputs } from '@nx/devkit/src/utils/get-named-inputs';
|
||||
import { existsSync, readdirSync } from 'fs';
|
||||
import { globWithWorkspaceContext } from 'nx/src/utils/workspace-context';
|
||||
|
||||
import { calculateHashForCreateNodes } from '@nx/devkit/src/utils/calculate-hash-for-create-nodes';
|
||||
import { projectGraphCacheDirectory } from 'nx/src/utils/cache-directory';
|
||||
import { NX_PLUGIN_OPTIONS } from '../utils/constants';
|
||||
import { loadConfigFile } from '@nx/devkit/src/utils/config-utils';
|
||||
import { hashObject } from 'nx/src/devkit-internals';
|
||||
import { globWithWorkspaceContext } from 'nx/src/utils/workspace-context';
|
||||
|
||||
export interface CypressPluginOptions {
|
||||
ciTargetName?: string;
|
||||
@ -98,9 +99,12 @@ async function createNodesInternal(
|
||||
return {};
|
||||
}
|
||||
|
||||
const hash = calculateHashForCreateNodes(projectRoot, options, context, [
|
||||
getLockFileName(detectPackageManager(context.workspaceRoot)),
|
||||
]);
|
||||
const hash = await calculateHashForCreateNodes(
|
||||
projectRoot,
|
||||
options,
|
||||
context,
|
||||
[getLockFileName(detectPackageManager(context.workspaceRoot))]
|
||||
);
|
||||
|
||||
targetsCache[hash] ??= await buildCypressTargets(
|
||||
configFilePath,
|
||||
@ -237,7 +241,7 @@ async function buildCypressTargets(
|
||||
: Array.isArray(cypressConfig.e2e.excludeSpecPattern)
|
||||
? cypressConfig.e2e.excludeSpecPattern.map((p) => join(projectRoot, p))
|
||||
: [join(projectRoot, cypressConfig.e2e.excludeSpecPattern)];
|
||||
const specFiles = globWithWorkspaceContext(
|
||||
const specFiles = await globWithWorkspaceContext(
|
||||
context.workspaceRoot,
|
||||
specPatterns,
|
||||
excludeSpecPatterns
|
||||
|
||||
@ -42,7 +42,7 @@ export const createDependencies: CreateDependencies = () => {
|
||||
|
||||
export const createNodes: CreateNodes<DetoxPluginOptions> = [
|
||||
'**/{detox.config,.detoxrc}.{json,js}',
|
||||
(configFilePath, options, context) => {
|
||||
async (configFilePath, options, context) => {
|
||||
options = normalizeOptions(options);
|
||||
const projectRoot = dirname(configFilePath);
|
||||
|
||||
@ -52,9 +52,12 @@ export const createNodes: CreateNodes<DetoxPluginOptions> = [
|
||||
return {};
|
||||
}
|
||||
|
||||
const hash = calculateHashForCreateNodes(projectRoot, options, context, [
|
||||
getLockFileName(detectPackageManager(context.workspaceRoot)),
|
||||
]);
|
||||
const hash = await calculateHashForCreateNodes(
|
||||
projectRoot,
|
||||
options,
|
||||
context,
|
||||
[getLockFileName(detectPackageManager(context.workspaceRoot))]
|
||||
);
|
||||
|
||||
targetsCache[hash] ??= buildDetoxTargets(projectRoot, options, context);
|
||||
|
||||
|
||||
@ -3,14 +3,14 @@ import { CreateNodesContext, hashArray } from 'nx/src/devkit-exports';
|
||||
|
||||
import { hashObject, hashWithWorkspaceContext } from 'nx/src/devkit-internals';
|
||||
|
||||
export function calculateHashForCreateNodes(
|
||||
export async function calculateHashForCreateNodes(
|
||||
projectRoot: string,
|
||||
options: object,
|
||||
context: CreateNodesContext,
|
||||
additionalGlobs: string[] = []
|
||||
): string {
|
||||
): Promise<string> {
|
||||
return hashArray([
|
||||
hashWithWorkspaceContext(context.workspaceRoot, [
|
||||
await hashWithWorkspaceContext(context.workspaceRoot, [
|
||||
join(projectRoot, '**/*'),
|
||||
...additionalGlobs,
|
||||
]),
|
||||
|
||||
@ -3,6 +3,8 @@ import { createTreeWithEmptyWorkspace } from 'nx/src/devkit-testing-exports';
|
||||
import { WORKSPACE_PLUGIN_DIR } from '../../constants';
|
||||
import update from './rename-workspace-rules';
|
||||
|
||||
import 'nx/src/internal-testing-utils/mock-project-graph';
|
||||
|
||||
const rule1Name = 'test-rule';
|
||||
const rule2Name = 'my-rule';
|
||||
|
||||
|
||||
@ -3,6 +3,7 @@ import 'nx/src/internal-testing-utils/mock-project-graph';
|
||||
import { NxJsonConfiguration, readJson, Tree, updateJson } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
import { LinterInitOptions, lintInitGenerator } from './init';
|
||||
import { setWorkspaceRoot } from 'nx/src/utils/workspace-root';
|
||||
|
||||
describe('@nx/eslint:init', () => {
|
||||
let tree: Tree;
|
||||
@ -10,6 +11,7 @@ describe('@nx/eslint:init', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace({ layout: 'apps-libs' });
|
||||
setWorkspaceRoot(tree.root);
|
||||
options = {
|
||||
addPlugin: true,
|
||||
};
|
||||
|
||||
@ -51,7 +51,7 @@ export const createNodes: CreateNodes<EslintPluginOptions> = [
|
||||
}
|
||||
}
|
||||
|
||||
const projectFiles = globWithWorkspaceContext(
|
||||
const projectFiles = await globWithWorkspaceContext(
|
||||
context.workspaceRoot,
|
||||
[
|
||||
'project.json',
|
||||
@ -77,7 +77,7 @@ export const createNodes: CreateNodes<EslintPluginOptions> = [
|
||||
const nestedProjectRootPatterns = excludePatterns.slice(index + 1);
|
||||
|
||||
// Ignore project roots where the project does not contain any lintable files
|
||||
const lintableFiles = globWithWorkspaceContext(
|
||||
const lintableFiles = await globWithWorkspaceContext(
|
||||
context.workspaceRoot,
|
||||
[join(childProjectRoot, `**/*.{${options.extensions.join(',')}}`)],
|
||||
// exclude nested eslint roots and nested project roots
|
||||
|
||||
@ -72,9 +72,12 @@ export const createNodes: CreateNodes<ExpoPluginOptions> = [
|
||||
return {};
|
||||
}
|
||||
|
||||
const hash = calculateHashForCreateNodes(projectRoot, options, context, [
|
||||
getLockFileName(detectPackageManager(context.workspaceRoot)),
|
||||
]);
|
||||
const hash = await calculateHashForCreateNodes(
|
||||
projectRoot,
|
||||
options,
|
||||
context,
|
||||
[getLockFileName(detectPackageManager(context.workspaceRoot))]
|
||||
);
|
||||
|
||||
targetsCache[hash] ??= buildExpoTargets(projectRoot, options, context);
|
||||
|
||||
|
||||
@ -72,7 +72,7 @@ export const createNodesV2: CreateNodesV2<GradlePluginOptions> = [
|
||||
);
|
||||
const targetsCache = readTargetsCache(cachePath);
|
||||
|
||||
populateGradleReport(context.workspaceRoot);
|
||||
await populateGradleReport(context.workspaceRoot);
|
||||
const gradleReport = getCurrentGradleReport();
|
||||
|
||||
try {
|
||||
@ -93,14 +93,14 @@ export const makeCreateNodes =
|
||||
gradleReport: GradleReport,
|
||||
targetsCache: GradleTargets
|
||||
): CreateNodesFunction =>
|
||||
(
|
||||
async (
|
||||
gradleFilePath,
|
||||
options: GradlePluginOptions | undefined,
|
||||
context: CreateNodesContext
|
||||
) => {
|
||||
const projectRoot = dirname(gradleFilePath);
|
||||
|
||||
const hash = calculateHashForCreateNodes(
|
||||
const hash = await calculateHashForCreateNodes(
|
||||
projectRoot,
|
||||
options ?? {},
|
||||
context
|
||||
@ -128,14 +128,14 @@ export const makeCreateNodes =
|
||||
*/
|
||||
export const createNodes: CreateNodes<GradlePluginOptions> = [
|
||||
gradleConfigGlob,
|
||||
(configFile, options, context) => {
|
||||
async (configFile, options, context) => {
|
||||
logger.warn(
|
||||
'`createNodes` is deprecated. Update your plugin to utilize createNodesV2 instead. In Nx 20, this will change to the createNodesV2 API.'
|
||||
);
|
||||
populateGradleReport(context.workspaceRoot);
|
||||
await populateGradleReport(context.workspaceRoot);
|
||||
const gradleReport = getCurrentGradleReport();
|
||||
const internalCreateNodes = makeCreateNodes(gradleReport, {});
|
||||
return internalCreateNodes(configFile, options, context);
|
||||
return await internalCreateNodes(configFile, options, context);
|
||||
},
|
||||
];
|
||||
|
||||
|
||||
@ -36,8 +36,10 @@ export function getCurrentGradleReport() {
|
||||
return gradleReportCache;
|
||||
}
|
||||
|
||||
export function populateGradleReport(workspaceRoot: string): void {
|
||||
const gradleConfigHash = hashWithWorkspaceContext(workspaceRoot, [
|
||||
export async function populateGradleReport(
|
||||
workspaceRoot: string
|
||||
): Promise<void> {
|
||||
const gradleConfigHash = await hashWithWorkspaceContext(workspaceRoot, [
|
||||
gradleConfigGlob,
|
||||
]);
|
||||
if (gradleReportCache && gradleConfigHash === gradleCurrentConfigHash) {
|
||||
|
||||
@ -127,7 +127,7 @@ async function createNodesInternal(
|
||||
|
||||
options = normalizeOptions(options);
|
||||
|
||||
const hash = calculateHashForCreateNodes(projectRoot, options, context);
|
||||
const hash = await calculateHashForCreateNodes(projectRoot, options, context);
|
||||
targetsCache[hash] ??= await buildJestTargets(
|
||||
configFilePath,
|
||||
projectRoot,
|
||||
|
||||
@ -76,7 +76,7 @@ export const PLUGIN_NAME = '@nx/js/typescript';
|
||||
|
||||
export const createNodes: CreateNodes<TscPluginOptions> = [
|
||||
'**/tsconfig*.json',
|
||||
(configFilePath, options, context) => {
|
||||
async (configFilePath, options, context) => {
|
||||
const pluginOptions = normalizePluginOptions(options);
|
||||
const projectRoot = dirname(configFilePath);
|
||||
const fullConfigPath = joinPathFragments(
|
||||
@ -101,7 +101,7 @@ export const createNodes: CreateNodes<TscPluginOptions> = [
|
||||
return {};
|
||||
}
|
||||
|
||||
const nodeHash = calculateHashForCreateNodes(
|
||||
const nodeHash = await calculateHashForCreateNodes(
|
||||
projectRoot,
|
||||
pluginOptions,
|
||||
context,
|
||||
|
||||
@ -7,4 +7,5 @@ export default {
|
||||
globals: {},
|
||||
displayName: 'nest',
|
||||
preset: '../../jest.preset.js',
|
||||
setupFilesAfterEnv: ['<rootDir>/test-setup.ts'],
|
||||
};
|
||||
|
||||
12
packages/nest/test-setup.ts
Normal file
12
packages/nest/test-setup.ts
Normal file
@ -0,0 +1,12 @@
|
||||
// If a test uses a util from devkit, but that util
|
||||
// lives in the Nx package and creates the project graph,
|
||||
// we need to mock the resolved value inside the Nx package
|
||||
jest
|
||||
.spyOn(
|
||||
require('nx/src/project-graph/project-graph'),
|
||||
'createProjectGraphAsync'
|
||||
)
|
||||
.mockResolvedValue({
|
||||
nodes: {},
|
||||
dependencies: {},
|
||||
});
|
||||
@ -11,7 +11,8 @@
|
||||
"**/*.test.ts",
|
||||
"**/*_spec.ts",
|
||||
"**/*_test.ts",
|
||||
"jest.config.ts"
|
||||
"jest.config.ts",
|
||||
"test-setup.ts"
|
||||
],
|
||||
"include": ["**/*.ts"]
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
"**/*.spec.jsx",
|
||||
"**/*.test.jsx",
|
||||
"**/*.d.ts",
|
||||
"jest.config.ts"
|
||||
"jest.config.ts",
|
||||
"test-setup.ts"
|
||||
]
|
||||
}
|
||||
|
||||
@ -63,9 +63,12 @@ export const createNodes: CreateNodes<NextPluginOptions> = [
|
||||
}
|
||||
options = normalizeOptions(options);
|
||||
|
||||
const hash = calculateHashForCreateNodes(projectRoot, options, context, [
|
||||
getLockFileName(detectPackageManager(context.workspaceRoot)),
|
||||
]);
|
||||
const hash = await calculateHashForCreateNodes(
|
||||
projectRoot,
|
||||
options,
|
||||
context,
|
||||
[getLockFileName(detectPackageManager(context.workspaceRoot))]
|
||||
);
|
||||
|
||||
targetsCache[hash] ??= await buildNextTargets(
|
||||
configFilePath,
|
||||
|
||||
@ -62,9 +62,12 @@ export const createNodes: CreateNodes<NuxtPluginOptions> = [
|
||||
|
||||
options = normalizeOptions(options);
|
||||
|
||||
const hash = calculateHashForCreateNodes(projectRoot, options, context, [
|
||||
getLockFileName(detectPackageManager(context.workspaceRoot)),
|
||||
]);
|
||||
const hash = await calculateHashForCreateNodes(
|
||||
projectRoot,
|
||||
options,
|
||||
context,
|
||||
[getLockFileName(detectPackageManager(context.workspaceRoot))]
|
||||
);
|
||||
targetsCache[hash] ??= await buildNuxtTargets(
|
||||
configFilePath,
|
||||
projectRoot,
|
||||
|
||||
@ -5,6 +5,14 @@ import {
|
||||
import { createTreeWithEmptyWorkspace } from '../generators/testing-utils/create-tree-with-empty-workspace';
|
||||
import { addProjectConfiguration } from '../generators/utils/project-configuration';
|
||||
|
||||
jest.mock('../project-graph/project-graph', () => ({
|
||||
...jest.requireActual('../project-graph/project-graph'),
|
||||
createProjectGraphAsync: () => ({
|
||||
nodes: {},
|
||||
externalNodes: {},
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('ngcli-adapter', () => {
|
||||
it('arrayBufferToString should support large buffers', () => {
|
||||
const largeString = 'a'.repeat(1000000);
|
||||
|
||||
@ -290,7 +290,7 @@ async function addBundler(options: NormalizedOptions) {
|
||||
options.isStandalone,
|
||||
options.appIsJs
|
||||
);
|
||||
renameJsToJsx(options.reactAppName, options.isStandalone);
|
||||
await renameJsToJsx(options.reactAppName, options.isStandalone);
|
||||
} else {
|
||||
output.log({ title: '🧑🔧 Setting up craco + Webpack' });
|
||||
const { addCracoCommandsToPackageScripts } = await import(
|
||||
|
||||
@ -3,8 +3,8 @@ import { globWithWorkspaceContext } from '../../../../utils/workspace-context';
|
||||
import { fileExists } from '../../../../utils/fileutils';
|
||||
|
||||
// Vite cannot process JSX like <div> or <Header> unless the file is named .jsx or .tsx
|
||||
export function renameJsToJsx(appName: string, isStandalone: boolean) {
|
||||
const files = globWithWorkspaceContext(process.cwd(), [
|
||||
export async function renameJsToJsx(appName: string, isStandalone: boolean) {
|
||||
const files = await globWithWorkspaceContext(process.cwd(), [
|
||||
isStandalone ? 'src/**/*.js' : `apps/${appName}/src/**/*.js`,
|
||||
]);
|
||||
|
||||
|
||||
@ -168,7 +168,7 @@ async function detectPlugins(): Promise<{
|
||||
updatePackageScripts: boolean;
|
||||
}> {
|
||||
let files = ['package.json'].concat(
|
||||
globWithWorkspaceContext(process.cwd(), ['**/*/package.json'])
|
||||
await globWithWorkspaceContext(process.cwd(), ['**/*/package.json'])
|
||||
);
|
||||
|
||||
const detectedPlugins = new Set<string>();
|
||||
|
||||
@ -42,7 +42,7 @@ describe('Workspaces', () => {
|
||||
readNxJson(fs.tempDir).plugins,
|
||||
fs.tempDir
|
||||
);
|
||||
const res = retrieveProjectConfigurations(
|
||||
const res = await retrieveProjectConfigurations(
|
||||
plugins,
|
||||
fs.tempDir,
|
||||
readNxJson(fs.tempDir)
|
||||
|
||||
@ -4,7 +4,7 @@ import { readFileSync, statSync } from 'fs';
|
||||
import { FileHandle, open } from 'fs/promises';
|
||||
import { ensureDirSync, ensureFileSync } from 'fs-extra';
|
||||
import { connect } from 'net';
|
||||
import { join } from 'path';
|
||||
import { extname, join } from 'path';
|
||||
import { performance } from 'perf_hooks';
|
||||
import { output } from '../../utils/output';
|
||||
import { getFullOsSocketPath, killSocketOrPath } from '../socket-utils';
|
||||
@ -30,6 +30,21 @@ import {
|
||||
ProjectGraphError,
|
||||
} from '../../project-graph/error-types';
|
||||
import { loadRootEnvFiles } from '../../utils/dotenv';
|
||||
import { HandleGlobMessage } from '../message-types/glob';
|
||||
import {
|
||||
GET_NX_WORKSPACE_FILES,
|
||||
HandleNxWorkspaceFilesMessage,
|
||||
} from '../message-types/get-nx-workspace-files';
|
||||
import {
|
||||
GET_CONTEXT_FILE_DATA,
|
||||
HandleContextFileDataMessage,
|
||||
} from '../message-types/get-context-file-data';
|
||||
import {
|
||||
GET_FILES_IN_DIRECTORY,
|
||||
HandleGetFilesInDirectoryMessage,
|
||||
} from '../message-types/get-files-in-directory';
|
||||
import { HASH_GLOB, HandleHashGlobMessage } from '../message-types/hash-glob';
|
||||
import { NxWorkspaceFiles } from '../../native';
|
||||
|
||||
const DAEMON_ENV_SETTINGS = {
|
||||
NX_PROJECT_GLOB_CACHE: 'false',
|
||||
@ -256,6 +271,49 @@ export class DaemonClient {
|
||||
});
|
||||
}
|
||||
|
||||
glob(globs: string[], exclude?: string[]): Promise<string[]> {
|
||||
const message: HandleGlobMessage = {
|
||||
type: 'GLOB',
|
||||
globs,
|
||||
exclude,
|
||||
};
|
||||
return this.sendToDaemonViaQueue(message);
|
||||
}
|
||||
|
||||
getWorkspaceContextFileData(): Promise<FileData[]> {
|
||||
const message: HandleContextFileDataMessage = {
|
||||
type: GET_CONTEXT_FILE_DATA,
|
||||
};
|
||||
return this.sendToDaemonViaQueue(message);
|
||||
}
|
||||
|
||||
getWorkspaceFiles(
|
||||
projectRootMap: Record<string, string>
|
||||
): Promise<NxWorkspaceFiles> {
|
||||
const message: HandleNxWorkspaceFilesMessage = {
|
||||
type: GET_NX_WORKSPACE_FILES,
|
||||
projectRootMap,
|
||||
};
|
||||
return this.sendToDaemonViaQueue(message);
|
||||
}
|
||||
|
||||
getFilesInDirectory(dir: string): Promise<string[]> {
|
||||
const message: HandleGetFilesInDirectoryMessage = {
|
||||
type: GET_FILES_IN_DIRECTORY,
|
||||
dir,
|
||||
};
|
||||
return this.sendToDaemonViaQueue(message);
|
||||
}
|
||||
|
||||
hashGlob(globs: string[], exclude?: string[]): Promise<string> {
|
||||
const message: HandleHashGlobMessage = {
|
||||
type: HASH_GLOB,
|
||||
globs,
|
||||
exclude,
|
||||
};
|
||||
return this.sendToDaemonViaQueue(message);
|
||||
}
|
||||
|
||||
async isServerAvailable(): Promise<boolean> {
|
||||
return new Promise((resolve) => {
|
||||
try {
|
||||
@ -414,14 +472,17 @@ export class DaemonClient {
|
||||
|
||||
const backgroundProcess = spawn(
|
||||
process.execPath,
|
||||
[join(__dirname, '../server/start.js')],
|
||||
[join(__dirname, `../server/start.js`)],
|
||||
{
|
||||
cwd: workspaceRoot,
|
||||
stdio: ['ignore', this._out.fd, this._err.fd],
|
||||
detached: true,
|
||||
windowsHide: true,
|
||||
shell: false,
|
||||
env: { ...process.env, ...DAEMON_ENV_SETTINGS },
|
||||
env: {
|
||||
...process.env,
|
||||
...DAEMON_ENV_SETTINGS,
|
||||
},
|
||||
}
|
||||
);
|
||||
backgroundProcess.unref();
|
||||
|
||||
3
packages/nx/src/daemon/is-on-daemon.ts
Normal file
3
packages/nx/src/daemon/is-on-daemon.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export function isOnDaemon() {
|
||||
return !!global.NX_DAEMON;
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
export const GET_CONTEXT_FILE_DATA = 'GET_CONTEXT_FILE_DATA' as const;
|
||||
|
||||
export type HandleContextFileDataMessage = {
|
||||
type: typeof GET_CONTEXT_FILE_DATA;
|
||||
};
|
||||
|
||||
export function isHandleContextFileDataMessage(
|
||||
message: unknown
|
||||
): message is HandleContextFileDataMessage {
|
||||
return (
|
||||
typeof message === 'object' &&
|
||||
message !== null &&
|
||||
'type' in message &&
|
||||
message['type'] === GET_CONTEXT_FILE_DATA
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
export const GET_FILES_IN_DIRECTORY = 'GET_FILES_IN_DIRECTORY' as const;
|
||||
|
||||
export type HandleGetFilesInDirectoryMessage = {
|
||||
type: typeof GET_FILES_IN_DIRECTORY;
|
||||
dir: string;
|
||||
};
|
||||
|
||||
export function isHandleGetFilesInDirectoryMessage(
|
||||
message: unknown
|
||||
): message is HandleGetFilesInDirectoryMessage {
|
||||
return (
|
||||
typeof message === 'object' &&
|
||||
message !== null &&
|
||||
'type' in message &&
|
||||
message['type'] === GET_FILES_IN_DIRECTORY
|
||||
);
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
export const GET_NX_WORKSPACE_FILES = 'GET_NX_WORKSPACE_FILES' as const;
|
||||
|
||||
export type HandleNxWorkspaceFilesMessage = {
|
||||
type: typeof GET_NX_WORKSPACE_FILES;
|
||||
projectRootMap: Record<string, string>;
|
||||
};
|
||||
|
||||
export function isHandleNxWorkspaceFilesMessage(
|
||||
message: unknown
|
||||
): message is HandleNxWorkspaceFilesMessage {
|
||||
return (
|
||||
typeof message === 'object' &&
|
||||
message !== null &&
|
||||
'type' in message &&
|
||||
message['type'] === GET_NX_WORKSPACE_FILES
|
||||
);
|
||||
}
|
||||
18
packages/nx/src/daemon/message-types/glob.ts
Normal file
18
packages/nx/src/daemon/message-types/glob.ts
Normal file
@ -0,0 +1,18 @@
|
||||
export const GLOB = 'GLOB' as const;
|
||||
|
||||
export type HandleGlobMessage = {
|
||||
type: typeof GLOB;
|
||||
globs: string[];
|
||||
exclude?: string[];
|
||||
};
|
||||
|
||||
export function isHandleGlobMessage(
|
||||
message: unknown
|
||||
): message is HandleGlobMessage {
|
||||
return (
|
||||
typeof message === 'object' &&
|
||||
message !== null &&
|
||||
'type' in message &&
|
||||
message['type'] === GLOB
|
||||
);
|
||||
}
|
||||
18
packages/nx/src/daemon/message-types/hash-glob.ts
Normal file
18
packages/nx/src/daemon/message-types/hash-glob.ts
Normal file
@ -0,0 +1,18 @@
|
||||
export const HASH_GLOB = 'HASH_GLOB' as const;
|
||||
|
||||
export type HandleHashGlobMessage = {
|
||||
type: typeof HASH_GLOB;
|
||||
globs: string[];
|
||||
exclude?: string[];
|
||||
};
|
||||
|
||||
export function isHandleHashGlobMessage(
|
||||
message: unknown
|
||||
): message is HandleHashGlobMessage {
|
||||
return (
|
||||
typeof message === 'object' &&
|
||||
message !== null &&
|
||||
'type' in message &&
|
||||
message['type'] === HASH_GLOB
|
||||
);
|
||||
}
|
||||
18
packages/nx/src/daemon/message-types/update-context-files.ts
Normal file
18
packages/nx/src/daemon/message-types/update-context-files.ts
Normal file
@ -0,0 +1,18 @@
|
||||
export const GLOB = 'GLOB' as const;
|
||||
|
||||
export type HandleUpdateContextMessage = {
|
||||
type: typeof GLOB;
|
||||
updatedFiles: string[];
|
||||
deletedFiles: string[];
|
||||
};
|
||||
|
||||
export function isHandleUpdateContextMessage(
|
||||
message: unknown
|
||||
): message is HandleUpdateContextMessage {
|
||||
return (
|
||||
typeof message === 'object' &&
|
||||
message !== null &&
|
||||
'type' in message &&
|
||||
message['type'] === GLOB
|
||||
);
|
||||
}
|
||||
11
packages/nx/src/daemon/server/handle-context-file-data.ts
Normal file
11
packages/nx/src/daemon/server/handle-context-file-data.ts
Normal file
@ -0,0 +1,11 @@
|
||||
import { getAllFileDataInContext } from '../../utils/workspace-context';
|
||||
import { workspaceRoot } from '../../utils/workspace-root';
|
||||
import { HandlerResult } from './server';
|
||||
|
||||
export async function handleContextFileData(): Promise<HandlerResult> {
|
||||
const files = await getAllFileDataInContext(workspaceRoot);
|
||||
return {
|
||||
response: JSON.stringify(files),
|
||||
description: 'handleContextFileData',
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,13 @@
|
||||
import { getFilesInDirectoryUsingContext } from '../../utils/workspace-context';
|
||||
import { workspaceRoot } from '../../utils/workspace-root';
|
||||
import { HandlerResult } from './server';
|
||||
|
||||
export async function handleGetFilesInDirectory(
|
||||
dir: string
|
||||
): Promise<HandlerResult> {
|
||||
const files = await getFilesInDirectoryUsingContext(workspaceRoot, dir);
|
||||
return {
|
||||
response: JSON.stringify(files),
|
||||
description: 'handleNxWorkspaceFiles',
|
||||
};
|
||||
}
|
||||
14
packages/nx/src/daemon/server/handle-glob.ts
Normal file
14
packages/nx/src/daemon/server/handle-glob.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { workspaceRoot } from '../../utils/workspace-root';
|
||||
import { globWithWorkspaceContext } from '../../utils/workspace-context';
|
||||
import { HandlerResult } from './server';
|
||||
|
||||
export async function handleGlob(
|
||||
globs: string[],
|
||||
exclude?: string[]
|
||||
): Promise<HandlerResult> {
|
||||
const files = await globWithWorkspaceContext(workspaceRoot, globs, exclude);
|
||||
return {
|
||||
response: JSON.stringify(files),
|
||||
description: 'handleGlob',
|
||||
};
|
||||
}
|
||||
14
packages/nx/src/daemon/server/handle-hash-glob.ts
Normal file
14
packages/nx/src/daemon/server/handle-hash-glob.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import { workspaceRoot } from '../../utils/workspace-root';
|
||||
import { hashWithWorkspaceContext } from '../../utils/workspace-context';
|
||||
import { HandlerResult } from './server';
|
||||
|
||||
export async function handleHashGlob(
|
||||
globs: string[],
|
||||
exclude?: string[]
|
||||
): Promise<HandlerResult> {
|
||||
const files = await hashWithWorkspaceContext(workspaceRoot, globs, exclude);
|
||||
return {
|
||||
response: JSON.stringify(files),
|
||||
description: 'handleHashGlob',
|
||||
};
|
||||
}
|
||||
16
packages/nx/src/daemon/server/handle-nx-workspace-files.ts
Normal file
16
packages/nx/src/daemon/server/handle-nx-workspace-files.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { getNxWorkspaceFilesFromContext } from '../../utils/workspace-context';
|
||||
import { workspaceRoot } from '../../utils/workspace-root';
|
||||
import { HandlerResult } from './server';
|
||||
|
||||
export async function handleNxWorkspaceFiles(
|
||||
projectRootMap: Record<string, string>
|
||||
): Promise<HandlerResult> {
|
||||
const files = await getNxWorkspaceFilesFromContext(
|
||||
workspaceRoot,
|
||||
projectRootMap
|
||||
);
|
||||
return {
|
||||
response: JSON.stringify(files),
|
||||
description: 'handleNxWorkspaceFiles',
|
||||
};
|
||||
}
|
||||
@ -1,10 +0,0 @@
|
||||
import { getAllFileDataInContext } from '../../utils/workspace-context';
|
||||
import { workspaceRoot } from '../../utils/workspace-root';
|
||||
|
||||
export async function handleRequestFileData() {
|
||||
const response = JSON.stringify(getAllFileDataInContext(workspaceRoot));
|
||||
return {
|
||||
response,
|
||||
description: 'handleRequestFileData',
|
||||
};
|
||||
}
|
||||
@ -26,7 +26,6 @@ import {
|
||||
handleRecordOutputsHash,
|
||||
} from './handle-outputs-tracking';
|
||||
import { handleProcessInBackground } from './handle-process-in-background';
|
||||
import { handleRequestFileData } from './handle-request-file-data';
|
||||
import { handleRequestProjectGraph } from './handle-request-project-graph';
|
||||
import { handleRequestShutdown } from './handle-request-shutdown';
|
||||
import { serverLogger } from './logger';
|
||||
@ -52,11 +51,32 @@ import {
|
||||
watchOutputFiles,
|
||||
watchWorkspace,
|
||||
} from './watcher';
|
||||
import { handleGlob } from './handle-glob';
|
||||
import { GLOB, isHandleGlobMessage } from '../message-types/glob';
|
||||
import {
|
||||
GET_NX_WORKSPACE_FILES,
|
||||
isHandleNxWorkspaceFilesMessage,
|
||||
} from '../message-types/get-nx-workspace-files';
|
||||
import { handleNxWorkspaceFiles } from './handle-nx-workspace-files';
|
||||
import {
|
||||
GET_CONTEXT_FILE_DATA,
|
||||
isHandleContextFileDataMessage,
|
||||
} from '../message-types/get-context-file-data';
|
||||
import { handleContextFileData } from './handle-context-file-data';
|
||||
import {
|
||||
GET_FILES_IN_DIRECTORY,
|
||||
isHandleGetFilesInDirectoryMessage,
|
||||
} from '../message-types/get-files-in-directory';
|
||||
import { handleGetFilesInDirectory } from './handle-get-files-in-directory';
|
||||
import { HASH_GLOB, isHandleHashGlobMessage } from '../message-types/hash-glob';
|
||||
import { handleHashGlob } from './handle-hash-glob';
|
||||
|
||||
let performanceObserver: PerformanceObserver | undefined;
|
||||
let workspaceWatcherError: Error | undefined;
|
||||
let outputsWatcherError: Error | undefined;
|
||||
|
||||
global.NX_DAEMON = true;
|
||||
|
||||
export type HandlerResult = {
|
||||
description: string;
|
||||
error?: any;
|
||||
@ -111,11 +131,12 @@ async function handleMessage(socket, data: string) {
|
||||
);
|
||||
}
|
||||
|
||||
if (daemonIsOutdated()) {
|
||||
const outdated = daemonIsOutdated();
|
||||
if (outdated) {
|
||||
await respondWithErrorAndExit(
|
||||
socket,
|
||||
`Lock files changed`,
|
||||
new Error('LOCK-FILES-CHANGED')
|
||||
`Daemon outdated`,
|
||||
new Error(outdated)
|
||||
);
|
||||
}
|
||||
|
||||
@ -143,10 +164,6 @@ async function handleMessage(socket, data: string) {
|
||||
);
|
||||
} else if (payload.type === 'HASH_TASKS') {
|
||||
await handleResult(socket, 'HASH_TASKS', () => handleHashTasks(payload));
|
||||
} else if (payload.type === 'REQUEST_FILE_DATA') {
|
||||
await handleResult(socket, 'REQUEST_FILE_DATA', () =>
|
||||
handleRequestFileData()
|
||||
);
|
||||
} else if (payload.type === 'PROCESS_IN_BACKGROUND') {
|
||||
await handleResult(socket, 'PROCESS_IN_BACKGROUND', () =>
|
||||
handleProcessInBackground(payload)
|
||||
@ -165,6 +182,26 @@ async function handleMessage(socket, data: string) {
|
||||
);
|
||||
} else if (payload.type === 'REGISTER_FILE_WATCHER') {
|
||||
registeredFileWatcherSockets.push({ socket, config: payload.config });
|
||||
} else if (isHandleGlobMessage(payload)) {
|
||||
await handleResult(socket, GLOB, () =>
|
||||
handleGlob(payload.globs, payload.exclude)
|
||||
);
|
||||
} else if (isHandleNxWorkspaceFilesMessage(payload)) {
|
||||
await handleResult(socket, GET_NX_WORKSPACE_FILES, () =>
|
||||
handleNxWorkspaceFiles(payload.projectRootMap)
|
||||
);
|
||||
} else if (isHandleGetFilesInDirectoryMessage(payload)) {
|
||||
await handleResult(socket, GET_FILES_IN_DIRECTORY, () =>
|
||||
handleGetFilesInDirectory(payload.dir)
|
||||
);
|
||||
} else if (isHandleContextFileDataMessage(payload)) {
|
||||
await handleResult(socket, GET_CONTEXT_FILE_DATA, () =>
|
||||
handleContextFileData()
|
||||
);
|
||||
} else if (isHandleHashGlobMessage(payload)) {
|
||||
await handleResult(socket, HASH_GLOB, () =>
|
||||
handleHashGlob(payload.globs, payload.exclude)
|
||||
);
|
||||
} else {
|
||||
await respondWithErrorAndExit(
|
||||
socket,
|
||||
@ -233,8 +270,13 @@ function registerProcessTerminationListeners() {
|
||||
|
||||
let existingLockHash: string | undefined;
|
||||
|
||||
function daemonIsOutdated(): boolean {
|
||||
return nxVersionChanged() || lockFileHashChanged();
|
||||
function daemonIsOutdated(): string | null {
|
||||
if (nxVersionChanged()) {
|
||||
return 'NX_VERSION_CHANGED';
|
||||
} else if (lockFileHashChanged()) {
|
||||
return 'LOCK_FILES_CHANGED';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function nxVersionChanged(): boolean {
|
||||
@ -291,10 +333,11 @@ const handleWorkspaceChanges: FileWatcherCallback = async (
|
||||
try {
|
||||
resetInactivityTimeout(handleInactivityTimeout);
|
||||
|
||||
if (daemonIsOutdated()) {
|
||||
const outdatedReason = daemonIsOutdated();
|
||||
if (outdatedReason) {
|
||||
await handleServerProcessTermination({
|
||||
server,
|
||||
reason: 'Lock file changed',
|
||||
reason: outdatedReason,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@ -132,7 +132,7 @@ export {
|
||||
/**
|
||||
* @category Generators
|
||||
*/
|
||||
export { glob } from './generators/utils/glob';
|
||||
export { glob, globAsync } from './generators/utils/glob';
|
||||
|
||||
/**
|
||||
* @category Generators
|
||||
|
||||
@ -1,7 +1,28 @@
|
||||
import { minimatch } from 'minimatch';
|
||||
import { Tree } from '../tree';
|
||||
import { combineGlobPatterns } from '../../utils/globs';
|
||||
import { globWithWorkspaceContext } from '../../utils/workspace-context';
|
||||
import {
|
||||
globWithWorkspaceContext,
|
||||
globWithWorkspaceContextSync,
|
||||
} from '../../utils/workspace-context';
|
||||
|
||||
/**
|
||||
* Performs a tree-aware glob search on the files in a workspace. Able to find newly
|
||||
* created files and hides deleted files before the updates are committed to disk.
|
||||
* Paths should be unix-style with forward slashes.
|
||||
*
|
||||
* @param tree The file system tree
|
||||
* @param patterns A list of glob patterns
|
||||
* @returns Normalized paths in the workspace that match the provided glob patterns.
|
||||
* @deprecated Use {@link globAsync} instead.
|
||||
*/
|
||||
export function glob(tree: Tree, patterns: string[]): string[] {
|
||||
return combineGlobResultsWithTree(
|
||||
tree,
|
||||
patterns,
|
||||
globWithWorkspaceContextSync(tree.root, patterns)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a tree-aware glob search on the files in a workspace. Able to find newly
|
||||
@ -12,8 +33,23 @@ import { globWithWorkspaceContext } from '../../utils/workspace-context';
|
||||
* @param patterns A list of glob patterns
|
||||
* @returns Normalized paths in the workspace that match the provided glob patterns.
|
||||
*/
|
||||
export function glob(tree: Tree, patterns: string[]): string[] {
|
||||
const matches = new Set(globWithWorkspaceContext(tree.root, patterns));
|
||||
export async function globAsync(
|
||||
tree: Tree,
|
||||
patterns: string[]
|
||||
): Promise<string[]> {
|
||||
return combineGlobResultsWithTree(
|
||||
tree,
|
||||
patterns,
|
||||
await globWithWorkspaceContext(tree.root, patterns)
|
||||
);
|
||||
}
|
||||
|
||||
function combineGlobResultsWithTree(
|
||||
tree: Tree,
|
||||
patterns: string[],
|
||||
results: string[]
|
||||
) {
|
||||
const matches = new Set(results);
|
||||
|
||||
const combinedGlob = combineGlobPatterns(patterns);
|
||||
const matcher = minimatch.makeRe(combinedGlob);
|
||||
|
||||
@ -4,12 +4,8 @@ import { basename, join, relative } from 'path';
|
||||
import {
|
||||
buildProjectConfigurationFromPackageJson,
|
||||
getGlobPatternsFromPackageManagerWorkspaces,
|
||||
createNodes as packageJsonWorkspacesCreateNodes,
|
||||
} from '../../plugins/package-json-workspaces';
|
||||
import {
|
||||
buildProjectFromProjectJson,
|
||||
ProjectJsonProjectsPlugin,
|
||||
} from '../../plugins/project-json/build-nodes/project-json';
|
||||
import { buildProjectFromProjectJson } from '../../plugins/project-json/build-nodes/project-json';
|
||||
import { renamePropertyWithStableKeys } from '../../adapter/angular-json';
|
||||
import {
|
||||
ProjectConfiguration,
|
||||
@ -19,8 +15,7 @@ import {
|
||||
mergeProjectConfigurationIntoRootMap,
|
||||
readProjectConfigurationsFromRootMap,
|
||||
} from '../../project-graph/utils/project-configuration-utils';
|
||||
import { configurationGlobs } from '../../project-graph/utils/retrieve-workspace-files';
|
||||
import { globWithWorkspaceContext } from '../../utils/workspace-context';
|
||||
import { globWithWorkspaceContextSync } from '../../utils/workspace-context';
|
||||
import { output } from '../../utils/output';
|
||||
import { PackageJson } from '../../utils/package-json';
|
||||
import { joinPathFragments, normalizePath } from '../../utils/path';
|
||||
@ -28,7 +23,6 @@ import { readJson, writeJson } from './json';
|
||||
import { readNxJson } from './nx-json';
|
||||
|
||||
import type { Tree } from '../tree';
|
||||
import { NxPlugin } from '../../project-graph/plugins';
|
||||
|
||||
export { readNxJson, updateNxJson } from './nx-json';
|
||||
|
||||
@ -200,7 +194,7 @@ function readAndCombineAllProjectConfigurations(tree: Tree): {
|
||||
readJson(tree, p, { expectComments: true })
|
||||
),
|
||||
];
|
||||
const globbedFiles = globWithWorkspaceContext(tree.root, patterns);
|
||||
const globbedFiles = globWithWorkspaceContextSync(tree.root, patterns);
|
||||
const createdFiles = findCreatedProjectFiles(tree, patterns);
|
||||
const deletedFiles = findDeletedProjectFiles(tree, patterns);
|
||||
const projectFiles = [...globbedFiles, ...createdFiles].filter(
|
||||
|
||||
@ -89,7 +89,7 @@ describe('explicit package json dependencies', () => {
|
||||
|
||||
const fileMap = createFileMap(
|
||||
projectsConfigurations as any,
|
||||
getAllFileDataInContext(tempFs.tempDir)
|
||||
await getAllFileDataInContext(tempFs.tempDir)
|
||||
).fileMap;
|
||||
|
||||
const builder = new ProjectGraphBuilder(undefined, fileMap.projectFileMap);
|
||||
|
||||
@ -39,12 +39,7 @@ export async function createFileMapUsingProjectGraph(
|
||||
): Promise<WorkspaceFileMap> {
|
||||
const configs = readProjectsConfigurationFromProjectGraph(graph);
|
||||
|
||||
let files: FileData[];
|
||||
if (daemonClient.enabled()) {
|
||||
files = await daemonClient.getAllFileData();
|
||||
} else {
|
||||
files = getAllFileDataInContext(workspaceRoot);
|
||||
}
|
||||
let files: FileData[] = await getAllFileDataInContext(workspaceRoot);
|
||||
|
||||
return createFileMap(configs, files);
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { execSync } from 'child_process';
|
||||
import { existsSync, readFileSync } from 'fs';
|
||||
import { extname, join, relative, sep } from 'path';
|
||||
import { basename, extname, join, relative, sep } from 'path';
|
||||
import { readNxJson } from '../config/configuration';
|
||||
import { FileData } from '../config/project-graph';
|
||||
import {
|
||||
@ -17,16 +17,18 @@ import {
|
||||
} from './project-graph';
|
||||
import { toOldFormat } from '../adapter/angular-json';
|
||||
import { getIgnoreObject } from '../utils/ignore';
|
||||
import { retrieveProjectConfigurationPaths } from './utils/retrieve-workspace-files';
|
||||
import {
|
||||
mergeProjectConfigurationIntoRootMap,
|
||||
readProjectConfigurationsFromRootMap,
|
||||
} from './utils/project-configuration-utils';
|
||||
import { NxJsonConfiguration } from '../config/nx-json';
|
||||
import { getDefaultPluginsSync } from '../utils/nx-plugin.deprecated';
|
||||
import { minimatch } from 'minimatch';
|
||||
import { CreateNodesResult } from '../devkit-exports';
|
||||
import { PackageJsonProjectsNextToProjectJsonPlugin } from '../plugins/project-json/build-nodes/package-json-next-to-project-json';
|
||||
import {
|
||||
buildProjectConfigurationFromPackageJson,
|
||||
getGlobPatternsFromPackageManagerWorkspaces,
|
||||
} from '../plugins/package-json-workspaces';
|
||||
import { globWithWorkspaceContextSync } from '../utils/workspace-context';
|
||||
import { buildProjectFromProjectJson } from '../plugins/project-json/build-nodes/project-json';
|
||||
import { PackageJson } from '../utils/package-json';
|
||||
import { NxJsonConfiguration } from '../devkit-exports';
|
||||
|
||||
export interface Change {
|
||||
type: string;
|
||||
@ -151,7 +153,7 @@ export function readWorkspaceConfig(opts: {
|
||||
} catch {
|
||||
configuration = {
|
||||
version: 2,
|
||||
projects: getProjectsSyncNoInference(root, nxJson).projects,
|
||||
projects: getProjectsSync(root, nxJson),
|
||||
};
|
||||
}
|
||||
if (opts.format === 'angularCli') {
|
||||
@ -179,50 +181,59 @@ export { FileData };
|
||||
/**
|
||||
* TODO(v20): Remove this function.
|
||||
*/
|
||||
function getProjectsSyncNoInference(root: string, nxJson: NxJsonConfiguration) {
|
||||
const allConfigFiles = retrieveProjectConfigurationPaths(
|
||||
root,
|
||||
getDefaultPluginsSync(root)
|
||||
);
|
||||
const plugins = [
|
||||
PackageJsonProjectsNextToProjectJsonPlugin,
|
||||
...getDefaultPluginsSync(root),
|
||||
function getProjectsSync(
|
||||
root: string,
|
||||
nxJson: NxJsonConfiguration
|
||||
): {
|
||||
[name: string]: ProjectConfiguration;
|
||||
} {
|
||||
/**
|
||||
* We can't update projects that come from plugins anyways, so we are going
|
||||
* to ignore them for now. Plugins should add their own add/create/update methods
|
||||
* if they would like to use devkit to update inferred projects.
|
||||
*/
|
||||
const patterns = [
|
||||
'**/project.json',
|
||||
'project.json',
|
||||
...getGlobPatternsFromPackageManagerWorkspaces(root, readJsonFile),
|
||||
];
|
||||
const projectFiles = globWithWorkspaceContextSync(root, patterns);
|
||||
|
||||
const projectRootMap: Record<string, ProjectConfiguration> = {};
|
||||
|
||||
// We iterate over plugins first - this ensures that plugins specified first take precedence.
|
||||
for (const plugin of plugins) {
|
||||
const [pattern, createNodes] = plugin.createNodes ?? [];
|
||||
if (!pattern) {
|
||||
continue;
|
||||
}
|
||||
const matchingConfigFiles = allConfigFiles.filter((file) =>
|
||||
minimatch(file, pattern, { dot: true })
|
||||
);
|
||||
for (const file of matchingConfigFiles) {
|
||||
if (minimatch(file, pattern, { dot: true })) {
|
||||
let r = createNodes(
|
||||
file,
|
||||
{},
|
||||
const rootMap: Record<string, ProjectConfiguration> = {};
|
||||
for (const projectFile of projectFiles) {
|
||||
if (basename(projectFile) === 'project.json') {
|
||||
const json = readJsonFile(projectFile);
|
||||
const config = buildProjectFromProjectJson(json, projectFile);
|
||||
mergeProjectConfigurationIntoRootMap(
|
||||
rootMap,
|
||||
config,
|
||||
undefined,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
} else if (basename(projectFile) === 'package.json') {
|
||||
const packageJson = readJsonFile<PackageJson>(projectFile);
|
||||
const config = buildProjectConfigurationFromPackageJson(
|
||||
packageJson,
|
||||
projectFile,
|
||||
nxJson
|
||||
);
|
||||
if (!rootMap[config.root]) {
|
||||
mergeProjectConfigurationIntoRootMap(
|
||||
rootMap,
|
||||
// Inferred targets, tags, etc don't show up when running generators
|
||||
// This is to help avoid running into issues when trying to update the workspace
|
||||
{
|
||||
nxJsonConfiguration: nxJson,
|
||||
workspaceRoot: root,
|
||||
configFiles: matchingConfigFiles,
|
||||
}
|
||||
) as CreateNodesResult;
|
||||
for (const node in r.projects) {
|
||||
const project = {
|
||||
root: node,
|
||||
...r.projects[node],
|
||||
};
|
||||
mergeProjectConfigurationIntoRootMap(projectRootMap, project);
|
||||
}
|
||||
name: config.name,
|
||||
root: config.root,
|
||||
},
|
||||
undefined,
|
||||
undefined,
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
projects: readProjectConfigurationsFromRootMap(projectRootMap),
|
||||
};
|
||||
return readProjectConfigurationsFromRootMap(rootMap);
|
||||
}
|
||||
|
||||
@ -28,17 +28,23 @@ export function loadRemoteNxPlugin(
|
||||
// but its typescript.
|
||||
const isWorkerTypescript = path.extname(__filename) === '.ts';
|
||||
const workerPath = path.join(__dirname, 'plugin-worker');
|
||||
|
||||
const env: Record<string, string> = {
|
||||
...process.env,
|
||||
...(isWorkerTypescript
|
||||
? {
|
||||
// Ensures that the worker uses the same tsconfig as the main process
|
||||
TS_NODE_PROJECT: path.join(
|
||||
__dirname,
|
||||
'../../../../tsconfig.lib.json'
|
||||
),
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
|
||||
const worker = fork(workerPath, [], {
|
||||
stdio: ['ignore', 'inherit', 'inherit', 'ipc'],
|
||||
env: {
|
||||
...process.env,
|
||||
...(isWorkerTypescript
|
||||
? {
|
||||
// Ensures that the worker uses the same tsconfig as the main process
|
||||
TS_NODE_PROJECT: path.join(__dirname, '../../../tsconfig.lib.json'),
|
||||
}
|
||||
: {}),
|
||||
},
|
||||
env,
|
||||
execArgv: [
|
||||
...process.execArgv,
|
||||
// If the worker is typescript, we need to register ts-node
|
||||
|
||||
@ -25,7 +25,7 @@ describe('retrieveProjectConfigurationPaths', () => {
|
||||
})
|
||||
);
|
||||
|
||||
const configPaths = retrieveProjectConfigurationPaths(fs.tempDir, [
|
||||
const configPaths = await retrieveProjectConfigurationPaths(fs.tempDir, [
|
||||
{
|
||||
createNodes: [
|
||||
'{project.json,**/project.json}',
|
||||
|
||||
@ -38,7 +38,7 @@ export async function retrieveWorkspaceFiles(
|
||||
performance.mark('get-workspace-files:start');
|
||||
|
||||
const { projectFileMap, globalFiles, externalReferences } =
|
||||
getNxWorkspaceFilesFromContext(workspaceRoot, projectRootMap);
|
||||
await getNxWorkspaceFilesFromContext(workspaceRoot, projectRootMap);
|
||||
performance.mark('get-workspace-files:end');
|
||||
performance.measure(
|
||||
'get-workspace-files',
|
||||
@ -60,13 +60,16 @@ export async function retrieveWorkspaceFiles(
|
||||
* Walk through the workspace and return `ProjectConfigurations`. Only use this if the projectFileMap is not needed.
|
||||
*/
|
||||
|
||||
export function retrieveProjectConfigurations(
|
||||
export async function retrieveProjectConfigurations(
|
||||
plugins: LoadedNxPlugin[],
|
||||
workspaceRoot: string,
|
||||
nxJson: NxJsonConfiguration
|
||||
): Promise<ConfigurationResult> {
|
||||
const globPatterns = configurationGlobs(plugins);
|
||||
const workspaceFiles = globWithWorkspaceContext(workspaceRoot, globPatterns);
|
||||
const workspaceFiles = await globWithWorkspaceContext(
|
||||
workspaceRoot,
|
||||
globPatterns
|
||||
);
|
||||
|
||||
return createProjectConfigurations(
|
||||
workspaceRoot,
|
||||
@ -98,7 +101,11 @@ export async function retrieveProjectConfigurationsWithAngularProjects(
|
||||
workspaceRoot
|
||||
);
|
||||
|
||||
const res = retrieveProjectConfigurations(plugins, workspaceRoot, nxJson);
|
||||
const res = await retrieveProjectConfigurations(
|
||||
plugins,
|
||||
workspaceRoot,
|
||||
nxJson
|
||||
);
|
||||
cleanup();
|
||||
return res;
|
||||
}
|
||||
@ -106,7 +113,7 @@ export async function retrieveProjectConfigurationsWithAngularProjects(
|
||||
export function retrieveProjectConfigurationPaths(
|
||||
root: string,
|
||||
plugins: Array<{ createNodes?: readonly [string, ...unknown[]] } & unknown>
|
||||
): string[] {
|
||||
): Promise<string[]> {
|
||||
const projectGlobPatterns = configurationGlobs(plugins);
|
||||
return globWithWorkspaceContext(root, projectGlobPatterns);
|
||||
}
|
||||
@ -122,7 +129,10 @@ export async function retrieveProjectConfigurationsWithoutPluginInference(
|
||||
): Promise<Record<string, ProjectConfiguration>> {
|
||||
const nxJson = readNxJson(root);
|
||||
const [plugins, cleanup] = await loadNxPlugins([]); // only load default plugins
|
||||
const projectGlobPatterns = retrieveProjectConfigurationPaths(root, plugins);
|
||||
const projectGlobPatterns = await retrieveProjectConfigurationPaths(
|
||||
root,
|
||||
plugins
|
||||
);
|
||||
const cacheKey = root + ',' + projectGlobPatterns.join(',');
|
||||
|
||||
if (projectsWithoutPluginCache.has(cacheKey)) {
|
||||
@ -130,7 +140,7 @@ export async function retrieveProjectConfigurationsWithoutPluginInference(
|
||||
}
|
||||
|
||||
const projectFiles =
|
||||
globWithWorkspaceContext(root, projectGlobPatterns) ?? [];
|
||||
(await globWithWorkspaceContext(root, projectGlobPatterns)) ?? [];
|
||||
const { projects } = await createProjectConfigurations(
|
||||
root,
|
||||
nxJson,
|
||||
|
||||
@ -1,12 +1,7 @@
|
||||
import { FileData } from '../config/project-graph';
|
||||
import { daemonClient } from '../daemon/client/client';
|
||||
import { getAllFileDataInContext } from './workspace-context';
|
||||
import { workspaceRoot } from './workspace-root';
|
||||
|
||||
export function allFileData(): Promise<FileData[]> {
|
||||
if (daemonClient.enabled()) {
|
||||
return daemonClient.getAllFileData();
|
||||
} else {
|
||||
return Promise.resolve(getAllFileDataInContext(workspaceRoot));
|
||||
}
|
||||
return getAllFileDataInContext(workspaceRoot);
|
||||
}
|
||||
|
||||
@ -1,6 +1,8 @@
|
||||
import type { NxWorkspaceFilesExternals, WorkspaceContext } from '../native';
|
||||
import { performance } from 'perf_hooks';
|
||||
import { cacheDirectoryForWorkspace } from './cache-directory';
|
||||
import { isOnDaemon } from '../daemon/is-on-daemon';
|
||||
import { daemonClient } from '../daemon/client/client';
|
||||
|
||||
let workspaceContext: WorkspaceContext | undefined;
|
||||
|
||||
@ -20,15 +22,25 @@ export function setupWorkspaceContext(workspaceRoot: string) {
|
||||
);
|
||||
}
|
||||
|
||||
export function getNxWorkspaceFilesFromContext(
|
||||
export async function getNxWorkspaceFilesFromContext(
|
||||
workspaceRoot: string,
|
||||
projectRootMap: Record<string, string>
|
||||
) {
|
||||
ensureContextAvailable(workspaceRoot);
|
||||
return workspaceContext.getWorkspaceFiles(projectRootMap);
|
||||
if (isOnDaemon() || !daemonClient.enabled()) {
|
||||
ensureContextAvailable(workspaceRoot);
|
||||
return workspaceContext.getWorkspaceFiles(projectRootMap);
|
||||
}
|
||||
return daemonClient.getWorkspaceFiles(projectRootMap);
|
||||
}
|
||||
|
||||
export function globWithWorkspaceContext(
|
||||
/**
|
||||
* Sync method to get files matching globs from workspace context.
|
||||
* NOTE: This method will create the workspace context if it doesn't exist.
|
||||
* It should only be used within Nx internal in code paths that **must** be sync.
|
||||
* If used in an isolated plugin thread this will cause the workspace context
|
||||
* to be recreated which is slow.
|
||||
*/
|
||||
export function globWithWorkspaceContextSync(
|
||||
workspaceRoot: string,
|
||||
globs: string[],
|
||||
exclude?: string[]
|
||||
@ -37,13 +49,29 @@ export function globWithWorkspaceContext(
|
||||
return workspaceContext.glob(globs, exclude);
|
||||
}
|
||||
|
||||
export function hashWithWorkspaceContext(
|
||||
export async function globWithWorkspaceContext(
|
||||
workspaceRoot: string,
|
||||
globs: string[],
|
||||
exclude?: string[]
|
||||
) {
|
||||
ensureContextAvailable(workspaceRoot);
|
||||
return workspaceContext.hashFilesMatchingGlob(globs, exclude);
|
||||
if (isOnDaemon() || !daemonClient.enabled()) {
|
||||
ensureContextAvailable(workspaceRoot);
|
||||
return workspaceContext.glob(globs, exclude);
|
||||
} else {
|
||||
return daemonClient.glob(globs, exclude);
|
||||
}
|
||||
}
|
||||
|
||||
export async function hashWithWorkspaceContext(
|
||||
workspaceRoot: string,
|
||||
globs: string[],
|
||||
exclude?: string[]
|
||||
) {
|
||||
if (isOnDaemon() || !daemonClient.enabled()) {
|
||||
ensureContextAvailable(workspaceRoot);
|
||||
return workspaceContext.hashFilesMatchingGlob(globs, exclude);
|
||||
}
|
||||
return daemonClient.hashGlob(globs, exclude);
|
||||
}
|
||||
|
||||
export function updateFilesInContext(
|
||||
@ -53,17 +81,23 @@ export function updateFilesInContext(
|
||||
return workspaceContext?.incrementalUpdate(updatedFiles, deletedFiles);
|
||||
}
|
||||
|
||||
export function getAllFileDataInContext(workspaceRoot: string) {
|
||||
ensureContextAvailable(workspaceRoot);
|
||||
return workspaceContext.allFileData();
|
||||
export async function getAllFileDataInContext(workspaceRoot: string) {
|
||||
if (isOnDaemon() || !daemonClient.enabled()) {
|
||||
ensureContextAvailable(workspaceRoot);
|
||||
return workspaceContext.allFileData();
|
||||
}
|
||||
return daemonClient.getWorkspaceContextFileData();
|
||||
}
|
||||
|
||||
export function getFilesInDirectoryUsingContext(
|
||||
export async function getFilesInDirectoryUsingContext(
|
||||
workspaceRoot: string,
|
||||
dir: string
|
||||
) {
|
||||
ensureContextAvailable(workspaceRoot);
|
||||
return workspaceContext.getFilesInDirectory(dir);
|
||||
if (isOnDaemon() || !daemonClient.enabled()) {
|
||||
ensureContextAvailable(workspaceRoot);
|
||||
return workspaceContext.getFilesInDirectory(dir);
|
||||
}
|
||||
return daemonClient.getFilesInDirectory(dir);
|
||||
}
|
||||
|
||||
export function updateProjectFiles(
|
||||
|
||||
@ -108,9 +108,12 @@ async function createNodesInternal(
|
||||
|
||||
const normalizedOptions = normalizeOptions(options);
|
||||
|
||||
const hash = calculateHashForCreateNodes(projectRoot, options, context, [
|
||||
getLockFileName(detectPackageManager(context.workspaceRoot)),
|
||||
]);
|
||||
const hash = await calculateHashForCreateNodes(
|
||||
projectRoot,
|
||||
options,
|
||||
context,
|
||||
[getLockFileName(detectPackageManager(context.workspaceRoot))]
|
||||
);
|
||||
|
||||
targetsCache[hash] ??= await buildPlaywrightTargets(
|
||||
configFilePath,
|
||||
@ -199,7 +202,7 @@ async function buildPlaywrightTargets(
|
||||
playwrightConfig.testMatch ??= '**/*.@(spec|test).?(c|m)[jt]s?(x)';
|
||||
|
||||
const dependsOn: TargetConfiguration['dependsOn'] = [];
|
||||
forEachTestFile(
|
||||
await forEachTestFile(
|
||||
(testFile) => {
|
||||
const relativeSpecFilePath = normalizePath(
|
||||
relative(projectRoot, testFile)
|
||||
@ -246,7 +249,7 @@ async function buildPlaywrightTargets(
|
||||
return { targets, metadata };
|
||||
}
|
||||
|
||||
function forEachTestFile(
|
||||
async function forEachTestFile(
|
||||
cb: (path: string) => void,
|
||||
opts: {
|
||||
context: CreateNodesContext;
|
||||
@ -254,7 +257,7 @@ function forEachTestFile(
|
||||
config: PlaywrightTestConfig;
|
||||
}
|
||||
) {
|
||||
const files = getFilesInDirectoryUsingContext(
|
||||
const files = await getFilesInDirectoryUsingContext(
|
||||
opts.context.workspaceRoot,
|
||||
opts.path
|
||||
);
|
||||
|
||||
@ -70,9 +70,12 @@ export const createNodes: CreateNodes<ReactNativePluginOptions> = [
|
||||
return {};
|
||||
}
|
||||
|
||||
const hash = calculateHashForCreateNodes(projectRoot, options, context, [
|
||||
getLockFileName(detectPackageManager(context.workspaceRoot)),
|
||||
]);
|
||||
const hash = await calculateHashForCreateNodes(
|
||||
projectRoot,
|
||||
options,
|
||||
context,
|
||||
[getLockFileName(detectPackageManager(context.workspaceRoot))]
|
||||
);
|
||||
|
||||
targetsCache[hash] ??= buildReactNativeTargets(
|
||||
projectRoot,
|
||||
|
||||
@ -66,9 +66,12 @@ export const createNodes: CreateNodes<RemixPluginOptions> = [
|
||||
|
||||
options = normalizeOptions(options);
|
||||
|
||||
const hash = calculateHashForCreateNodes(projectRoot, options, context, [
|
||||
getLockFileName(detectPackageManager(context.workspaceRoot)),
|
||||
]);
|
||||
const hash = await calculateHashForCreateNodes(
|
||||
projectRoot,
|
||||
options,
|
||||
context,
|
||||
[getLockFileName(detectPackageManager(context.workspaceRoot))]
|
||||
);
|
||||
targetsCache[hash] ??= await buildRemixTargets(
|
||||
configFilePath,
|
||||
projectRoot,
|
||||
|
||||
@ -6,10 +6,39 @@ import {
|
||||
|
||||
describe('createWatchPaths', () => {
|
||||
it('should list root paths of dependencies relative to project root', async () => {
|
||||
const testDir = joinPathFragments(workspaceRoot, 'e2e/remix');
|
||||
// This test is written based on the Nx repo's project graph.
|
||||
jest
|
||||
.spyOn(require('@nx/devkit'), 'createProjectGraphAsync')
|
||||
.mockResolvedValue({
|
||||
nodes: {
|
||||
parent: {
|
||||
type: 'app',
|
||||
name: 'parent',
|
||||
data: { root: 'apps/parent' },
|
||||
},
|
||||
lib: {
|
||||
type: 'lib',
|
||||
name: 'lib',
|
||||
data: { root: 'packages/lib' },
|
||||
},
|
||||
example: {
|
||||
type: 'app',
|
||||
name: 'example',
|
||||
data: { root: 'examples/example' },
|
||||
},
|
||||
},
|
||||
dependencies: {
|
||||
parent: [
|
||||
{ type: 'static', source: 'parent', target: 'lib' },
|
||||
{ type: 'static', source: 'parent', target: 'example' },
|
||||
],
|
||||
example: [{ type: 'static', source: 'example', target: 'lib' }],
|
||||
},
|
||||
});
|
||||
const testDir = joinPathFragments(workspaceRoot, 'apps/parent');
|
||||
|
||||
const paths = await createWatchPaths(testDir);
|
||||
expect(paths).toEqual(['../../packages', '../../graph', '../../e2e/utils']);
|
||||
expect(paths).toEqual(['../../packages', '../../examples']);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@ -63,9 +63,12 @@ export const createNodes: CreateNodes<RollupPluginOptions> = [
|
||||
|
||||
options = normalizeOptions(options);
|
||||
|
||||
const hash = calculateHashForCreateNodes(projectRoot, options, context, [
|
||||
getLockFileName(detectPackageManager(context.workspaceRoot)),
|
||||
]);
|
||||
const hash = await calculateHashForCreateNodes(
|
||||
projectRoot,
|
||||
options,
|
||||
context,
|
||||
[getLockFileName(detectPackageManager(context.workspaceRoot))]
|
||||
);
|
||||
|
||||
targetsCache[hash] ??= await buildRollupTarget(
|
||||
configFilePath,
|
||||
|
||||
@ -72,9 +72,12 @@ export const createNodes: CreateNodes<StorybookPluginOptions> = [
|
||||
}
|
||||
|
||||
options = normalizeOptions(options);
|
||||
const hash = calculateHashForCreateNodes(projectRoot, options, context, [
|
||||
getLockFileName(detectPackageManager(context.workspaceRoot)),
|
||||
]);
|
||||
const hash = await calculateHashForCreateNodes(
|
||||
projectRoot,
|
||||
options,
|
||||
context,
|
||||
[getLockFileName(detectPackageManager(context.workspaceRoot))]
|
||||
);
|
||||
|
||||
const projectName = buildProjectName(projectRoot, context.workspaceRoot);
|
||||
|
||||
|
||||
@ -44,9 +44,10 @@ describe('@nx/vite/plugin', () => {
|
||||
production: ['!{projectRoot}/**/*.spec.ts'],
|
||||
},
|
||||
},
|
||||
workspaceRoot: '',
|
||||
workspaceRoot: tempFs.tempDir,
|
||||
};
|
||||
tempFs.createFileSync('index.html', '');
|
||||
tempFs.createFileSync('package.json', '');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
|
||||
@ -6,7 +6,6 @@ import {
|
||||
joinPathFragments,
|
||||
readJsonFile,
|
||||
TargetConfiguration,
|
||||
workspaceRoot,
|
||||
writeJsonFile,
|
||||
} from '@nx/devkit';
|
||||
import { dirname, isAbsolute, join, relative } from 'path';
|
||||
@ -118,7 +117,8 @@ async function buildViteTargets(
|
||||
|
||||
const { buildOutputs, testOutputs, hasTest, isBuildable } = getOutputs(
|
||||
viteConfig,
|
||||
projectRoot
|
||||
projectRoot,
|
||||
context.workspaceRoot
|
||||
);
|
||||
|
||||
const namedInputs = getNamedInputs(projectRoot, context);
|
||||
@ -244,7 +244,8 @@ function serveStaticTarget(options: VitePluginOptions) {
|
||||
|
||||
function getOutputs(
|
||||
viteConfig: Record<string, any> | undefined,
|
||||
projectRoot: string
|
||||
projectRoot: string,
|
||||
workspaceRoot: string
|
||||
): {
|
||||
buildOutputs: string[];
|
||||
testOutputs: string[];
|
||||
@ -256,6 +257,7 @@ function getOutputs(
|
||||
const buildOutputPath = normalizeOutputPath(
|
||||
build?.outDir,
|
||||
projectRoot,
|
||||
workspaceRoot,
|
||||
'dist'
|
||||
);
|
||||
|
||||
@ -267,6 +269,7 @@ function getOutputs(
|
||||
const reportsDirectoryPath = normalizeOutputPath(
|
||||
test?.coverage?.reportsDirectory,
|
||||
projectRoot,
|
||||
workspaceRoot,
|
||||
'coverage'
|
||||
);
|
||||
|
||||
@ -281,6 +284,7 @@ function getOutputs(
|
||||
function normalizeOutputPath(
|
||||
outputPath: string | undefined,
|
||||
projectRoot: string,
|
||||
workspaceRoot: string,
|
||||
path: 'coverage' | 'dist'
|
||||
): string | undefined {
|
||||
if (!outputPath) {
|
||||
|
||||
@ -69,9 +69,12 @@ export const createNodes: CreateNodes<WebpackPluginOptions> = [
|
||||
return {};
|
||||
}
|
||||
|
||||
const hash = calculateHashForCreateNodes(projectRoot, options, context, [
|
||||
getLockFileName(detectPackageManager(context.workspaceRoot)),
|
||||
]);
|
||||
const hash = await calculateHashForCreateNodes(
|
||||
projectRoot,
|
||||
options,
|
||||
context,
|
||||
[getLockFileName(detectPackageManager(context.workspaceRoot))]
|
||||
);
|
||||
const targets = targetsCache[hash]
|
||||
? targetsCache[hash]
|
||||
: await createWebpackTargets(
|
||||
|
||||
34
scripts/unit-test-setup.js
Normal file
34
scripts/unit-test-setup.js
Normal file
@ -0,0 +1,34 @@
|
||||
module.exports = () => {
|
||||
/**
|
||||
* When the daemon is enabled during unit tests,
|
||||
* and the daemon is already running, the daemon-client.ts
|
||||
* code will be used, but it will hit the already running
|
||||
* daemon which is from the installed version of Nx.
|
||||
*
|
||||
* In the vast majority of cases, this is fine. However,
|
||||
* if a new message type has been added to the daemon in
|
||||
* the source code, and isn't yet in the installed version,
|
||||
* any test that hits that codepath will fail. This is because
|
||||
* the installed version of the daemon doesn't know how to
|
||||
* handle the new message type.
|
||||
*
|
||||
* To prevent this, we disable the daemon during unit tests.
|
||||
*/
|
||||
process.env.NX_DAEMON = 'false';
|
||||
|
||||
/**
|
||||
* When `createProjectGraphAsync` is called during tests,
|
||||
* if its not mocked, it will return the Nx repo's project
|
||||
* graph. We don't want any unit tests to depend on the structure
|
||||
* of the Nx repo, so we mock it to return an empty project graph.
|
||||
*/
|
||||
jest.doMock('@nx/devkit', () => ({
|
||||
...jest.requireActual('@nx/devkit'),
|
||||
createProjectGraphAsync: jest.fn().mockImplementation(async () => {
|
||||
return {
|
||||
nodes: {},
|
||||
dependencies: {},
|
||||
};
|
||||
}),
|
||||
}));
|
||||
};
|
||||
@ -1,5 +1,13 @@
|
||||
const nxPreset = require('@nx/jest/preset').default;
|
||||
|
||||
/* eslint-disable */
|
||||
export default {
|
||||
...nxPreset,
|
||||
testTimeout: 35000,
|
||||
testMatch: ['**/+(*.)+(spec|test).+(ts|js)?(x)'],
|
||||
coverageReporters: ['html'],
|
||||
maxWorkers: 1,
|
||||
testEnvironment: 'node',
|
||||
displayName: 'typedoc-theme',
|
||||
|
||||
globals: {},
|
||||
@ -14,5 +22,5 @@ export default {
|
||||
resolver: '../scripts/patched-jest-resolver.js',
|
||||
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'],
|
||||
coverageDirectory: '../coverage/typedoc-theme',
|
||||
preset: '../jest.preset.js',
|
||||
setupFiles: [],
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user