UNPKG

nx

Version:

Smart, Fast and Extensible Build System

392 lines • 17.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getWorkspacePath = exports.shouldDefaultToUsingStandaloneConfigs = exports.readWorkspace = exports.getRelativeProjectJsonSchemaPath = exports.isStandaloneProject = exports.readNxJson = exports.readProjectConfiguration = exports.updateWorkspaceConfiguration = exports.readWorkspaceConfiguration = exports.getProjects = exports.removeProjectConfiguration = exports.updateProjectConfiguration = exports.addProjectConfiguration = void 0; const tslib_1 = require("tslib"); const path_1 = require("path"); const workspaces_1 = require("../../config/workspaces"); const path_2 = require("../../utils/path"); const json_1 = require("./json"); /** * Adds project configuration to the Nx workspace. * * The project configuration is stored in workspace.json or the associated project.json file. * The utility will update either files. * * @param tree - the file system tree * @param projectName - unique name. Often directories are part of the name (e.g., mydir-mylib) * @param projectConfiguration - project configuration * @param standalone - should the project use package.json? If false, the project config is inside workspace.json */ function addProjectConfiguration(tree, projectName, projectConfiguration, standalone) { standalone = standalone !== null && standalone !== void 0 ? standalone : shouldDefaultToUsingStandaloneConfigs(tree); setProjectConfiguration(tree, projectName, projectConfiguration, 'create', standalone); } exports.addProjectConfiguration = addProjectConfiguration; /** * Updates the configuration of an existing project. * * The project configuration is stored in workspace.json or the associated project.json file. * The utility will update either files. * * @param tree - the file system tree * @param projectName - unique name. Often directories are part of the name (e.g., mydir-mylib) * @param projectConfiguration - project configuration */ function updateProjectConfiguration(tree, projectName, projectConfiguration) { setProjectConfiguration(tree, projectName, projectConfiguration, 'update'); } exports.updateProjectConfiguration = updateProjectConfiguration; /** * Removes the configuration of an existing project. * * The project configuration is stored in workspace.json or the associated project.json file. * The utility will update either file. */ function removeProjectConfiguration(tree, projectName) { setProjectConfiguration(tree, projectName, undefined, 'delete'); } exports.removeProjectConfiguration = removeProjectConfiguration; /** * Get a map of all projects in a workspace. * * Use {@link readProjectConfiguration} if only one project is needed. */ function getProjects(tree) { const workspace = readWorkspace(tree); return new Map(Object.keys(workspace.projects || {}).map((projectName) => { return [projectName, getProjectConfiguration(projectName, workspace)]; })); } exports.getProjects = getProjects; /** * Read general workspace configuration such as the default project or cli settings * * This does _not_ provide projects configuration, use {@link readProjectConfiguration} instead. */ function readWorkspaceConfiguration(tree) { const _a = readRawWorkspaceJson(tree), { projects } = _a, workspace = tslib_1.__rest(_a, ["projects"]); // Create a new object, without projects let nxJson = readNxJson(tree); if (nxJson === null) { return workspace; } return Object.assign(Object.assign({}, workspace), nxJson); } exports.readWorkspaceConfiguration = readWorkspaceConfiguration; /** * Update general workspace configuration such as the default project or cli settings. * * This does _not_ update projects configuration, use {@link updateProjectConfiguration} or {@link addProjectConfiguration} instead. */ function updateWorkspaceConfiguration(tree, workspaceConfig) { const { // Nx Json Properties cli, defaultProject, generators, implicitDependencies, plugins, pluginsConfig, npmScope, namedInputs, targetDefaults, targetDependencies, workspaceLayout, tasksRunnerOptions, affected, extends: ext, } = workspaceConfig; const nxJson = { implicitDependencies, plugins, pluginsConfig, npmScope, namedInputs, targetDefaults, targetDependencies, workspaceLayout, tasksRunnerOptions, affected, cli, generators, defaultProject, extends: ext, }; if (tree.exists('nx.json')) { (0, json_1.updateJson)(tree, 'nx.json', (json) => { if (json.extends) { const nxJsonExtends = readNxJsonExtends(tree, json.extends); const changedPropsOfNxJson = {}; Object.keys(nxJson).forEach((prop) => { if (JSON.stringify(nxJson[prop], null, 2) != JSON.stringify(nxJsonExtends[prop], null, 2)) { changedPropsOfNxJson[prop] = nxJson[prop]; } }); return Object.assign(Object.assign({}, json), changedPropsOfNxJson); } else { return Object.assign(Object.assign({}, json), nxJson); } }); } // Only prop in workspace.json is version. If there is no // workspace.json file, this f(x) doesn't update anything // in project config. const workspacePath = getWorkspacePath(tree); if (workspacePath) { (0, json_1.updateJson)(tree, workspacePath, (json) => { const config = Object.assign(Object.assign({}, json), { version: workspaceConfig.version }); if (!workspaceConfig.newProjectRoot) { delete config.newProjectRoot; } return config; }); } } exports.updateWorkspaceConfiguration = updateWorkspaceConfiguration; function readNxJsonExtends(tree, extendsPath) { try { return (0, json_1.readJson)(tree, (0, path_1.relative)(tree.root, require.resolve(extendsPath, { paths: [tree.root], }))); } catch (e) { throw new Error(`Unable to resolve nx.json extends. Error: ${e.message}`); } } /** * Reads a project configuration. * * The project configuration is stored in workspace.json or the associated project.json file. * The utility will read from either file. * * @param tree - the file system tree * @param projectName - unique name. Often directories are part of the name (e.g., mydir-mylib) * @throws If supplied projectName cannot be found */ function readProjectConfiguration(tree, projectName) { const workspace = readWorkspace(tree); if (!workspace.projects[projectName]) { throw new Error(`Cannot find configuration for '${projectName}' in ${getWorkspacePath(tree)}.`); } return getProjectConfiguration(projectName, workspace); } exports.readProjectConfiguration = readProjectConfiguration; function readNxJson(tree) { if (!tree.exists('nx.json')) { return null; } let nxJson = (0, json_1.readJson)(tree, 'nx.json'); if (nxJson.extends) { nxJson = Object.assign(Object.assign({}, readNxJsonExtends(tree, nxJson.extends)), nxJson); } return nxJson; } exports.readNxJson = readNxJson; /** * Returns if a project has a standalone configuration (project.json). * * @param tree - the file system tree * @param project - the project name */ function isStandaloneProject(tree, project) { var _a; const path = getWorkspacePath(tree); const rawWorkspace = path && tree.exists(path) ? (0, json_1.readJson)(tree, path) : null; if (rawWorkspace) { const projectConfig = (_a = rawWorkspace.projects) === null || _a === void 0 ? void 0 : _a[project]; return typeof projectConfig === 'string'; } return true; } exports.isStandaloneProject = isStandaloneProject; function getProjectConfiguration(projectName, workspace) { return Object.assign({}, readWorkspaceSection(workspace, projectName)); } function readWorkspaceSection(workspace, projectName) { return workspace.projects[projectName]; } function setProjectConfiguration(tree, projectName, projectConfiguration, mode, standalone = false) { if (mode === 'delete') { addProjectToWorkspaceJson(tree, projectName, readProjectConfiguration(tree, projectName), mode); return; } if (!projectConfiguration) { throw new Error(`Cannot ${mode} "${projectName}" with value ${projectConfiguration}`); } addProjectToWorkspaceJson(tree, projectName, projectConfiguration, mode, standalone); } function getRelativeProjectJsonSchemaPath(tree, project) { return (0, path_2.normalizePath)((0, path_1.relative)((0, path_1.join)(tree.root, project.root), (0, path_1.join)(tree.root, 'node_modules/nx/schemas/project-schema.json'))); } exports.getRelativeProjectJsonSchemaPath = getRelativeProjectJsonSchemaPath; function addProjectToWorkspaceJson(tree, projectName, project, mode, standalone = false) { var _a; const workspaceConfigPath = getWorkspacePath(tree); const workspaceJson = readRawWorkspaceJson(tree); if (workspaceConfigPath) { validateProjectConfigurationOperationsGivenWorkspaceJson(mode, workspaceJson, projectName); } else { validateProjectConfigurationOperationsWithoutWorkspaceJson(mode, projectName, project.root, tree); } const configFile = (mode === 'create' && standalone) || !workspaceConfigPath ? (0, path_2.joinPathFragments)(project.root, 'project.json') : getProjectFileLocation(tree, projectName); const jsonSchema = configFile && mode === 'create' ? { $schema: getRelativeProjectJsonSchemaPath(tree, project) } : {}; if (configFile) { if (mode === 'delete') { tree.delete(configFile); delete workspaceJson.projects[projectName]; } else { // keep real workspace up to date if (workspaceConfigPath && mode === 'create') { workspaceJson.projects[projectName] = project.root; } // update the project.json file (0, json_1.writeJson)(tree, configFile, Object.assign(Object.assign(Object.assign({}, jsonSchema), project), { root: undefined })); } } else if (mode === 'delete') { delete workspaceJson.projects[projectName]; } else { workspaceJson.projects[projectName] = project; } if (workspaceConfigPath && tree.exists(workspaceConfigPath)) { (0, json_1.writeJson)(tree, workspaceConfigPath, (_a = (0, workspaces_1.reformattedWorkspaceJsonOrNull)(workspaceJson)) !== null && _a !== void 0 ? _a : workspaceJson); } } /** * Read the workspace configuration, including projects. */ function readWorkspace(tree) { const workspaceJson = inlineProjectConfigurationsWithTree(tree); const originalVersion = workspaceJson.version; return Object.assign(Object.assign({}, (0, workspaces_1.toNewFormat)(workspaceJson)), { version: originalVersion }); } exports.readWorkspace = readWorkspace; /** * This has to be separate from the inline functionality inside nx, * as the functionality in nx does not use a Tree. Changes made during * a generator would not be present during runtime execution. * @returns */ function inlineProjectConfigurationsWithTree(tree) { const workspaceJson = readRawWorkspaceJson(tree); Object.entries(workspaceJson.projects || {}).forEach(([project, config]) => { if (typeof config === 'string') { const configFileLocation = (0, path_2.joinPathFragments)(config, 'project.json'); workspaceJson.projects[project] = Object.assign({ root: config }, (0, json_1.readJson)(tree, configFileLocation)); } }); return workspaceJson; } /** * Used to ensure that projects created during * the same devkit generator run show up when * there is no workspace.json file, as `glob` * cannot find them. */ function findCreatedProjects(tree) { const files = tree .listChanges() .filter((f) => { const fileName = (0, path_1.basename)(f.path); return (f.type === 'CREATE' && (fileName === 'project.json' || fileName === 'package.json')); }) .map((x) => x.path); return (0, workspaces_1.deduplicateProjectFiles)(files); } /** * Used to ensure that projects created during * the same devkit generator run show up when * there is no workspace.json file, as `glob` * cannot find them. */ function findDeletedProjects(tree) { return tree.listChanges().filter((f) => { const fileName = (0, path_1.basename)(f.path); return (f.type === 'DELETE' && (fileName === 'project.json' || fileName === 'package.json')); }); } let staticFSWorkspace; function readRawWorkspaceJson(tree) { const path = getWorkspacePath(tree); if (path && tree.exists(path)) { // `workspace.json` exists, use it. return (0, json_1.readJson)(tree, path); } else { const nxJson = readNxJson(tree); const createdProjects = (0, workspaces_1.buildWorkspaceConfigurationFromGlobs)(nxJson, findCreatedProjects(tree), (file) => (0, json_1.readJson)(tree, file)).projects; // We already have built a cache if (!staticFSWorkspace) { staticFSWorkspace = (0, workspaces_1.buildWorkspaceConfigurationFromGlobs)(nxJson, [...(0, workspaces_1.globForProjectFiles)(tree.root, nxJson)], (file) => (0, json_1.readJson)(tree, file)); } const projects = Object.assign(Object.assign({}, staticFSWorkspace.projects), createdProjects); findDeletedProjects(tree).forEach((file) => { const matchingStaticProject = Object.entries(projects).find(([, config]) => typeof config === 'string' ? config === (0, path_1.dirname)(file.path) : config.root === (0, path_1.dirname)(file.path)); if (matchingStaticProject) { delete projects[matchingStaticProject[0]]; } }); return Object.assign(Object.assign({}, staticFSWorkspace), { projects }); } } /** * @description Determine where a project's configuration is located. * @returns file path if separate from root config, null otherwise. */ function getProjectFileLocation(tree, project) { var _a; const rawWorkspace = readRawWorkspaceJson(tree); const projectConfig = (_a = rawWorkspace.projects) === null || _a === void 0 ? void 0 : _a[project]; return typeof projectConfig === 'string' ? (0, path_2.joinPathFragments)(projectConfig, 'project.json') : null; } function validateProjectConfigurationOperationsGivenWorkspaceJson(mode, workspaceJson, projectName) { if (mode == 'create' && workspaceJson.projects[projectName]) { throw new Error(`Cannot create Project '${projectName}'. It already exists.`); } if (mode == 'update' && !workspaceJson.projects[projectName]) { throw new Error(`Cannot update Project '${projectName}'. It does not exist.`); } if (mode == 'delete' && !workspaceJson.projects[projectName]) { throw new Error(`Cannot delete Project '${projectName}'. It does not exist.`); } } function validateProjectConfigurationOperationsWithoutWorkspaceJson(mode, projectName, projectRoot, tree) { if (mode == 'create' && tree.exists((0, path_2.joinPathFragments)(projectRoot, 'project.json'))) { throw new Error(`Cannot create a new project at ${projectRoot}. It already exists.`); } if (mode == 'update' && !tree.exists((0, path_2.joinPathFragments)(projectRoot, 'project.json'))) { throw new Error(`Cannot update Project ${projectName} at ${projectRoot}. It doesn't exist or uses package.json configuration.`); } if (mode == 'delete' && !tree.exists((0, path_2.joinPathFragments)(projectRoot))) { throw new Error(`Cannot delete Project ${projectName}. It doesn't exist or uses package.json configuration.`); } } function shouldDefaultToUsingStandaloneConfigs(tree) { const workspacePath = getWorkspacePath(tree); const rawWorkspace = workspacePath && tree.exists(workspacePath) ? (0, json_1.readJson)(tree, workspacePath) : null; return !rawWorkspace ? true // if workspace.json doesn't exist, all projects **must** be standalone : Object.values(rawWorkspace.projects).reduce( // default for second, third... projects should be based on all projects being defined as a path // for configuration read from ng schematics, this is determined by configFilePath's presence (allStandalone, next) => allStandalone && (typeof next === 'string' || 'configFilePath' in next), // default for first project should be true if using Nx Schema rawWorkspace.version > 1); } exports.shouldDefaultToUsingStandaloneConfigs = shouldDefaultToUsingStandaloneConfigs; function getWorkspacePath(tree) { const possibleFiles = [ '/angular.json', '/workspace.json', ]; return possibleFiles.filter((path) => tree.exists(path))[0]; } exports.getWorkspacePath = getWorkspacePath; //# sourceMappingURL=project-configuration.js.map