- Enable generating the new & minimal TS setup by default when generating the `ts` preset with CNW. The existing `NX_ADD_TS_PLUGIN` environment variable is kept with its default value inverted and set to `true`. It can be used to opt out of the new TS setup by running CNW with `NX_ADD_TS_PLUGIN=false`. - Throw an error when running generators that don't yet support the new TS setup. - We'll add support for those generators incrementally in follow-up PRs. <!-- Please make sure you have read the submission guidelines before posting an PR --> <!-- https://github.com/nrwl/nx/blob/master/CONTRIBUTING.md#-submitting-a-pr --> <!-- Please make sure that your commit message follows our format --> <!-- Example: `fix(nx): must begin with lowercase` --> <!-- If this is a particularly complex change or feature addition, you can request a dedicated Nx release for this pull request branch. Mention someone from the Nx team or the `@nrwl/nx-pipelines-reviewers` and they will confirm if the PR warrants its own release for testing purposes, and generate it for you if appropriate. --> ## Current Behavior <!-- This is the behavior we have today --> ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> <!-- Fixes NXC-1066 --> <!-- Fixes NXC-1068 --> ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #
436 lines
13 KiB
TypeScript
436 lines
13 KiB
TypeScript
import {
|
|
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';
|
|
import { connectToNxCloud } from 'nx/src/nx-cloud/generators/connect-to-nx-cloud/connect-to-nx-cloud';
|
|
import { createNxCloudOnboardingURL } from 'nx/src/nx-cloud/utilities/url-shorten';
|
|
|
|
type PresetInfo = {
|
|
generateAppCmd?: string;
|
|
generateLibCmd?: string;
|
|
generateNxReleaseInfo?: boolean;
|
|
learnMoreLink?: string;
|
|
};
|
|
|
|
// map from the preset to the name of the plugin s.t. the README can have a more
|
|
// meaningful generator command.
|
|
const presetToPluginMap: { [key in Preset]: PresetInfo } = {
|
|
[Preset.Apps]: {
|
|
learnMoreLink:
|
|
'https://nx.dev/getting-started/intro#learn-nx?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects',
|
|
},
|
|
[Preset.NPM]: {
|
|
generateNxReleaseInfo: true,
|
|
learnMoreLink:
|
|
'https://nx.dev/getting-started/tutorials/npm-workspaces-tutorial?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects',
|
|
},
|
|
[Preset.TS]: {
|
|
generateLibCmd: '@nx/js',
|
|
generateNxReleaseInfo: true,
|
|
learnMoreLink:
|
|
'https://nx.dev/nx-api/js?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects',
|
|
},
|
|
[Preset.WebComponents]: {
|
|
generateAppCmd: null,
|
|
learnMoreLink:
|
|
'https://nx.dev/getting-started/intro#learn-nx?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects',
|
|
},
|
|
[Preset.AngularMonorepo]: {
|
|
generateAppCmd: '@nx/angular',
|
|
generateLibCmd: '@nx/angular',
|
|
learnMoreLink:
|
|
'https://nx.dev/getting-started/tutorials/angular-monorepo-tutorial?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects',
|
|
},
|
|
[Preset.AngularStandalone]: {
|
|
generateAppCmd: '@nx/angular',
|
|
generateLibCmd: '@nx/angular',
|
|
learnMoreLink:
|
|
'https://nx.dev/getting-started/tutorials/angular-standalone-tutorial?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects',
|
|
},
|
|
[Preset.ReactMonorepo]: {
|
|
generateAppCmd: '@nx/react',
|
|
generateLibCmd: '@nx/react',
|
|
learnMoreLink:
|
|
'https://nx.dev/getting-started/tutorials/react-monorepo-tutorial?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects',
|
|
},
|
|
[Preset.ReactStandalone]: {
|
|
generateAppCmd: '@nx/react',
|
|
generateLibCmd: '@nx/react',
|
|
learnMoreLink:
|
|
'https://nx.dev/getting-started/tutorials/react-standalone-tutorial?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects',
|
|
},
|
|
[Preset.NextJsStandalone]: {
|
|
generateAppCmd: '@nx/next',
|
|
generateLibCmd: '@nx/react',
|
|
learnMoreLink:
|
|
'https://nx.dev/nx-api/next?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects',
|
|
},
|
|
[Preset.RemixMonorepo]: {
|
|
generateAppCmd: '@nx/remix',
|
|
generateLibCmd: '@nx/react',
|
|
learnMoreLink:
|
|
'https://nx.dev/nx-api/remix?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects',
|
|
},
|
|
[Preset.RemixStandalone]: {
|
|
generateAppCmd: '@nx/remix',
|
|
generateLibCmd: '@nx/react',
|
|
learnMoreLink:
|
|
'https://nx.dev/nx-api/remix?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects',
|
|
},
|
|
[Preset.ReactNative]: {
|
|
generateAppCmd: '@nx/react-native',
|
|
generateLibCmd: '@nx/react',
|
|
learnMoreLink:
|
|
'https://nx.dev/nx-api/react-native?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects',
|
|
},
|
|
[Preset.VueMonorepo]: {
|
|
generateAppCmd: '@nx/vue',
|
|
generateLibCmd: '@nx/vue',
|
|
learnMoreLink:
|
|
'https://nx.dev/getting-started/tutorials/vue-standalone-tutorial?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects',
|
|
},
|
|
[Preset.VueStandalone]: {
|
|
generateAppCmd: '@nx/vue',
|
|
generateLibCmd: '@nx/vue',
|
|
learnMoreLink:
|
|
'https://nx.dev/getting-started/tutorials/vue-standalone-tutorial?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects',
|
|
},
|
|
[Preset.Nuxt]: {
|
|
generateAppCmd: '@nx/nuxt',
|
|
generateLibCmd: '@nx/vue',
|
|
learnMoreLink:
|
|
'https://nx.dev/nx-api/nuxt?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects',
|
|
},
|
|
[Preset.NuxtStandalone]: {
|
|
generateAppCmd: '@nx/nuxt',
|
|
generateLibCmd: '@nx/vue',
|
|
learnMoreLink:
|
|
'https://nx.dev/nx-api/nuxt?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects',
|
|
},
|
|
[Preset.Expo]: {
|
|
generateAppCmd: '@nx/expo',
|
|
generateLibCmd: '@nx/react',
|
|
learnMoreLink:
|
|
'https://nx.dev/nx-api/expo?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects',
|
|
},
|
|
[Preset.NextJs]: {
|
|
generateAppCmd: '@nx/next',
|
|
generateLibCmd: '@nx/react',
|
|
learnMoreLink:
|
|
'https://nx.dev/nx-api/next?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects',
|
|
},
|
|
[Preset.Nest]: {
|
|
generateAppCmd: '@nx/nest',
|
|
generateLibCmd: '@nx/node',
|
|
learnMoreLink:
|
|
'https://nx.dev/nx-api/nest?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects',
|
|
},
|
|
[Preset.Express]: {
|
|
generateAppCmd: '@nx/express',
|
|
generateLibCmd: '@nx/node',
|
|
learnMoreLink:
|
|
'https://nx.dev/nx-api/express?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects',
|
|
},
|
|
[Preset.NodeStandalone]: {
|
|
generateAppCmd: '@nx/node',
|
|
generateLibCmd: '@nx/node',
|
|
learnMoreLink:
|
|
'https://nx.dev/nx-api/node?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects',
|
|
},
|
|
[Preset.NodeMonorepo]: {
|
|
generateAppCmd: '@nx/node',
|
|
generateLibCmd: '@nx/node',
|
|
learnMoreLink:
|
|
'https://nx.dev/nx-api/node?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects',
|
|
},
|
|
[Preset.TsStandalone]: {
|
|
generateAppCmd: null,
|
|
generateLibCmd: null,
|
|
generateNxReleaseInfo: true,
|
|
learnMoreLink:
|
|
'https://nx.dev/nx-api/js?utm_source=nx_project&utm_medium=readme&utm_campaign=nx_projects',
|
|
},
|
|
};
|
|
|
|
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);
|
|
createFiles(tree, options);
|
|
const nxJson = createNxJson(tree, options);
|
|
|
|
const token =
|
|
options.nxCloud !== 'skip'
|
|
? await connectToNxCloud(
|
|
tree,
|
|
{
|
|
installationSource: 'create-nx-workspace',
|
|
directory: options.directory,
|
|
github: options.useGitHub,
|
|
},
|
|
nxJson
|
|
)
|
|
: null;
|
|
|
|
await createReadme(tree, options, token);
|
|
|
|
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);
|
|
|
|
return token;
|
|
}
|
|
|
|
function setPresetProperty(tree: Tree, options: NormalizedSchema) {
|
|
if (options.preset === Preset.NPM) {
|
|
updateJson(tree, join(options.directory, 'nx.json'), (json) => {
|
|
addPropertyWithStableKeys(json, 'extends', 'nx/presets/npm.json');
|
|
return json;
|
|
});
|
|
}
|
|
}
|
|
|
|
function createNxJson(
|
|
tree: Tree,
|
|
{ directory, defaultBase, preset }: NormalizedSchema
|
|
) {
|
|
const nxJson: NxJsonConfiguration & { $schema: string } = {
|
|
$schema: './node_modules/nx/schemas/nx-schema.json',
|
|
defaultBase,
|
|
targetDefaults:
|
|
process.env.NX_ADD_PLUGINS === 'false'
|
|
? {
|
|
build: {
|
|
cache: true,
|
|
dependsOn: ['^build'],
|
|
},
|
|
lint: {
|
|
cache: true,
|
|
},
|
|
}
|
|
: undefined,
|
|
};
|
|
|
|
if (defaultBase === 'main') {
|
|
delete nxJson.defaultBase;
|
|
}
|
|
if (preset !== Preset.NPM) {
|
|
nxJson.namedInputs = {
|
|
default: ['{projectRoot}/**/*', 'sharedGlobals'],
|
|
production: ['default'],
|
|
sharedGlobals: [],
|
|
};
|
|
if (process.env.NX_ADD_PLUGINS === 'false') {
|
|
nxJson.targetDefaults.build.inputs = ['production', '^production'];
|
|
nxJson.useInferencePlugins = false;
|
|
}
|
|
}
|
|
|
|
writeJson<NxJsonConfiguration>(tree, join(directory, 'nx.json'), nxJson);
|
|
|
|
return 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.NuxtStandalone ||
|
|
options.preset === Preset.NodeStandalone ||
|
|
options.preset === Preset.NextJsStandalone ||
|
|
options.preset === Preset.RemixStandalone ||
|
|
options.preset === Preset.TsStandalone
|
|
? './files-root-app'
|
|
: (options.preset === Preset.TS &&
|
|
process.env.NX_ADD_PLUGINS !== 'false' &&
|
|
process.env.NX_ADD_TS_PLUGIN !== 'false') ||
|
|
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,
|
|
});
|
|
}
|
|
|
|
async function createReadme(
|
|
tree: Tree,
|
|
{ name, appName, directory, preset, nxCloud }: NormalizedSchema,
|
|
nxCloudToken?: string
|
|
) {
|
|
const formattedNames = names(name);
|
|
|
|
// default to an empty one for custom presets
|
|
const presetInfo: PresetInfo = presetToPluginMap[preset] ?? {
|
|
package: '',
|
|
generateLibCmd: null,
|
|
};
|
|
|
|
const nxCloudOnboardingUrl = nxCloudToken
|
|
? await createNxCloudOnboardingURL('readme', nxCloudToken)
|
|
: null;
|
|
|
|
generateFiles(tree, join(__dirname, './files-readme'), directory, {
|
|
formattedNames,
|
|
isJsStandalone: preset === Preset.TsStandalone,
|
|
isTsPreset: preset === Preset.TS,
|
|
isUsingNewTsSolutionSetup:
|
|
process.env.NX_ADD_PLUGINS !== 'false' &&
|
|
process.env.NX_ADD_TS_PLUGIN !== 'false',
|
|
isEmptyRepo: !appName,
|
|
appName,
|
|
generateAppCmd: presetInfo.generateAppCmd,
|
|
generateLibCmd: presetInfo.generateLibCmd,
|
|
generateNxReleaseInfo: presetInfo.generateNxReleaseInfo,
|
|
learnMoreLink: presetInfo.learnMoreLink,
|
|
serveCommand:
|
|
preset === Preset.NextJs || preset === Preset.NextJsStandalone
|
|
? 'dev'
|
|
: 'serve',
|
|
name,
|
|
nxCloud,
|
|
nxCloudOnboardingUrl,
|
|
});
|
|
}
|
|
|
|
// 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.NuxtStandalone ||
|
|
options.preset === Preset.NodeStandalone
|
|
) {
|
|
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.NextJsStandalone) {
|
|
updateJson(tree, join(options.directory, 'package.json'), (json) => {
|
|
Object.assign(json.scripts, {
|
|
dev: 'nx dev',
|
|
build: 'nx build',
|
|
start: 'nx start',
|
|
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,
|
|
nxCloud: options.nxCloud ?? 'skip',
|
|
};
|
|
}
|
|
|
|
function setUpWorkspacesInPackageJson(tree: Tree, options: NormalizedSchema) {
|
|
if (
|
|
options.preset === Preset.NPM ||
|
|
(options.preset === Preset.TS &&
|
|
process.env.NX_ADD_PLUGINS !== 'false' &&
|
|
process.env.NX_ADD_TS_PLUGIN !== 'false')
|
|
) {
|
|
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;
|
|
});
|
|
}
|
|
}
|
|
}
|