import { formatFiles, generateFiles, getPackageManagerVersion, names, NxJsonConfiguration, PackageManager, Tree, updateJson, writeJson, } from '@nx/devkit'; import { nxVersion } from '../../utils/versions'; import { join } from 'path'; import { Preset } from '../utils/presets'; import { deduceDefaultBase } from '../../utilities/default-base'; import { NormalizedSchema } from './new'; export async function generateWorkspaceFiles( tree: Tree, options: NormalizedSchema ) { if (!options.name) { throw new Error(`Invalid options, "name" is required.`); } // we need to check package manager version before the package.json is generated // since it might influence the version report const packageManagerVersion = getPackageManagerVersion( options.packageManager as PackageManager, tree.root ); options = normalizeOptions(options); createReadme(tree, options); createFiles(tree, options); createNxJson(tree, options); const [packageMajor] = packageManagerVersion.split('.'); if (options.packageManager === 'pnpm' && +packageMajor >= 7) { createNpmrc(tree, options); } else if (options.packageManager === 'yarn') { if (+packageMajor >= 2) { createYarnrcYml(tree, options); // avoids errors when using nested yarn projects tree.write(join(options.directory, 'yarn.lock'), ''); } } setPresetProperty(tree, options); addNpmScripts(tree, options); setUpWorkspacesInPackageJson(tree, options); await formatFiles(tree); } function setPresetProperty(tree: Tree, options: NormalizedSchema) { updateJson(tree, join(options.directory, 'nx.json'), (json) => { if (options.preset === Preset.NPM) { addPropertyWithStableKeys(json, 'extends', 'nx/presets/npm.json'); delete json.implicitDependencies; delete json.targetDefaults; } return json; }); } function createAppsAndLibsFolders(tree: Tree, options: NormalizedSchema) { if (options.preset === Preset.TS || options.preset === Preset.NPM) { tree.write(join(options.directory, 'packages/.gitkeep'), ''); } else if ( options.preset === Preset.AngularStandalone || options.preset === Preset.ReactStandalone || options.preset === Preset.VueStandalone || options.preset === Preset.NodeStandalone || options.preset === Preset.NextJsStandalone || options.preset === Preset.TsStandalone || options.isCustomPreset ) { // don't generate any folders } else { tree.write(join(options.directory, 'apps/.gitkeep'), ''); tree.write(join(options.directory, 'libs/.gitkeep'), ''); } } function createNxJson( tree: Tree, { directory, defaultBase, preset }: NormalizedSchema ) { const nxJson: NxJsonConfiguration & { $schema: string } = { $schema: './node_modules/nx/schemas/nx-schema.json', affected: { defaultBase, }, tasksRunnerOptions: { default: { runner: 'nx/tasks-runners/default', options: { cacheableOperations: ['build', 'lint', 'test', 'e2e'], }, }, }, workspaceLayout: { projectNameAndRootFormat: 'as-provided', }, }; nxJson.targetDefaults = { build: { dependsOn: ['^build'], }, }; if (defaultBase === 'main') { delete nxJson.affected; } if (preset !== Preset.NPM) { nxJson.namedInputs = { default: ['{projectRoot}/**/*', 'sharedGlobals'], production: ['default'], sharedGlobals: [], }; nxJson.targetDefaults.build.inputs = ['production', '^production']; } writeJson(tree, join(directory, 'nx.json'), nxJson); } function createFiles(tree: Tree, options: NormalizedSchema) { const formattedNames = names(options.name); const filesDirName = options.preset === Preset.AngularStandalone || options.preset === Preset.ReactStandalone || options.preset === Preset.VueStandalone || options.preset === Preset.NodeStandalone || options.preset === Preset.NextJsStandalone || options.preset === Preset.TsStandalone ? './files-root-app' : options.preset === Preset.NPM ? './files-package-based-repo' : './files-integrated-repo'; generateFiles(tree, join(__dirname, filesDirName), options.directory, { formattedNames, dot: '.', tmpl: '', cliCommand: 'nx', nxCli: false, ...(options as object), nxVersion, packageManager: options.packageManager, }); } function createReadme( tree: Tree, { name, appName, directory, preset }: NormalizedSchema ) { const formattedNames = names(name); generateFiles(tree, join(__dirname, './files-readme'), directory, { formattedNames, includeServe: preset !== Preset.TsStandalone, appName, name, }); } // ensure that pnpm install add all the missing peer deps function createNpmrc(tree: Tree, options: NormalizedSchema) { tree.write( join(options.directory, '.npmrc'), 'strict-peer-dependencies=false\nauto-install-peers=true\n' ); } // ensure that yarn (berry) install uses classic node linker function createYarnrcYml(tree: Tree, options: NormalizedSchema) { tree.write( join(options.directory, '.yarnrc.yml'), 'nodeLinker: node-modules\n' ); } function addNpmScripts(tree: Tree, options: NormalizedSchema) { if ( options.preset === Preset.AngularStandalone || options.preset === Preset.ReactStandalone || options.preset === Preset.VueStandalone || options.preset === Preset.NodeStandalone || options.preset === Preset.NextJsStandalone ) { updateJson(tree, join(options.directory, 'package.json'), (json) => { Object.assign(json.scripts, { start: 'nx serve', build: 'nx build', test: 'nx test', }); return json; }); } if (options.preset === Preset.TsStandalone) { updateJson(tree, join(options.directory, 'package.json'), (json) => { Object.assign(json.scripts, { build: 'nx build', test: 'nx test', }); return json; }); } } function addPropertyWithStableKeys(obj: any, key: string, value: string) { const copy = { ...obj }; Object.keys(obj).forEach((k) => { delete obj[k]; }); obj[key] = value; Object.keys(copy).forEach((k) => { obj[k] = copy[k]; }); } function normalizeOptions(options: NormalizedSchema) { let defaultBase = options.defaultBase || deduceDefaultBase(); const name = names(options.name).fileName; return { name, ...options, defaultBase, }; } function setUpWorkspacesInPackageJson(tree: Tree, options: NormalizedSchema) { if (options.preset === Preset.NPM) { if (options.packageManager === 'pnpm') { tree.write( join(options.directory, 'pnpm-workspace.yaml'), `packages: - 'packages/*' ` ); } else { updateJson(tree, join(options.directory, 'package.json'), (json) => { json.workspaces = ['packages/*']; return json; }); } } }