feat(gradle): add gradle init generator (#22245)
This commit is contained in:
parent
45e1d78a21
commit
6d83dd7ff0
@ -8,16 +8,17 @@ import {
|
||||
runCommand,
|
||||
uniq,
|
||||
updateFile,
|
||||
updateJson,
|
||||
} from '@nx/e2e/utils';
|
||||
import { execSync } from 'child_process';
|
||||
|
||||
describe('Gradle', () => {
|
||||
describe.each([{ type: 'kotlin' }, { type: 'groovy' }])(
|
||||
'$type',
|
||||
({ type }: { type: 'kotlin' | 'groovy' }) => {
|
||||
let gradleProjectName = uniq('my-gradle-project');
|
||||
|
||||
beforeAll(() => {
|
||||
newProject();
|
||||
createGradleProject(gradleProjectName);
|
||||
createGradleProject(gradleProjectName, type);
|
||||
});
|
||||
afterAll(() => cleanupProject());
|
||||
|
||||
@ -41,47 +42,55 @@ describe('Gradle', () => {
|
||||
});
|
||||
|
||||
it('should track dependencies for new app', () => {
|
||||
if (type === 'groovy') {
|
||||
createFile(
|
||||
'app2/build.gradle.kts',
|
||||
`
|
||||
plugins {
|
||||
id("gradleProject.kotlin-application-conventions")
|
||||
`app2/build.gradle`,
|
||||
`plugins {
|
||||
id 'gradleProject.groovy-application-conventions'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation project(':app')
|
||||
}`
|
||||
);
|
||||
} else {
|
||||
createFile(
|
||||
`app2/build.gradle.kts`,
|
||||
`plugins {
|
||||
id("gradleProject.kotlin-library-conventions")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":app"))
|
||||
}
|
||||
`
|
||||
}`
|
||||
);
|
||||
updateFile(`settings.gradle.kts`, (content) => {
|
||||
}
|
||||
updateFile(
|
||||
`settings.gradle${type === 'kotlin' ? '.kts' : ''}`,
|
||||
(content) => {
|
||||
content += `\r\ninclude("app2")`;
|
||||
return content;
|
||||
});
|
||||
}
|
||||
);
|
||||
const buildOutput = runCLI('build app2', { verbose: true });
|
||||
// app2 depends on app
|
||||
expect(buildOutput).toContain('nx run app:build');
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
function createGradleProject(projectName: string) {
|
||||
function createGradleProject(
|
||||
projectName: string,
|
||||
type: 'kotlin' | 'groovy' = 'kotlin'
|
||||
) {
|
||||
e2eConsoleLogger(`Using java version: ${execSync('java --version')}`);
|
||||
e2eConsoleLogger(`Using gradle version: ${execSync('gradle --version')}`);
|
||||
e2eConsoleLogger(execSync(`gradle help --task :init`).toString());
|
||||
e2eConsoleLogger(
|
||||
runCommand(
|
||||
`gradle init --type kotlin-application --dsl kotlin --project-name ${projectName} --package gradleProject --no-incubating --split-project`
|
||||
`gradle init --type ${type}-application --dsl ${type} --project-name ${projectName} --package gradleProject --no-incubating --split-project`
|
||||
)
|
||||
);
|
||||
updateJson('nx.json', (nxJson) => {
|
||||
nxJson.plugins = ['@nx/gradle'];
|
||||
return nxJson;
|
||||
});
|
||||
createFile(
|
||||
'build.gradle.kts',
|
||||
`allprojects {
|
||||
apply {
|
||||
plugin("project-report")
|
||||
}
|
||||
}`
|
||||
);
|
||||
runCLI(`add @nx/gradle`);
|
||||
}
|
||||
|
||||
11
packages/gradle/generators.json
Normal file
11
packages/gradle/generators.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "Nx Gradle",
|
||||
"version": "0.1",
|
||||
"generators": {
|
||||
"init": {
|
||||
"factory": "./src/generators/init/init#initGenerator",
|
||||
"schema": "./src/generators/init/schema.json",
|
||||
"description": "Initializes a Gradle project in the current workspace"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1 +1,2 @@
|
||||
export * from './plugin';
|
||||
export { initGenerator } from './src/generators/init/init';
|
||||
|
||||
@ -22,6 +22,14 @@
|
||||
"url": "https://github.com/nrwl/nx/issues"
|
||||
},
|
||||
"homepage": "https://nx.dev",
|
||||
"generators": "./generators.json",
|
||||
"exports": {
|
||||
".": "./index.js",
|
||||
"./package.json": "./package.json",
|
||||
"./migrations.json": "./migrations.json",
|
||||
"./generators.json": "./generators.json",
|
||||
"./plugin": "./plugin.js"
|
||||
},
|
||||
"nx-migrate": {
|
||||
"migrations": "./migrations.json"
|
||||
},
|
||||
|
||||
73
packages/gradle/src/generators/init/init.spec.ts
Normal file
73
packages/gradle/src/generators/init/init.spec.ts
Normal file
@ -0,0 +1,73 @@
|
||||
import { readNxJson, Tree, updateNxJson } from '@nx/devkit';
|
||||
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||
|
||||
import { initGenerator } from './init';
|
||||
|
||||
describe('@nx/gradle:init', () => {
|
||||
let tree: Tree;
|
||||
|
||||
beforeEach(() => {
|
||||
tree = createTreeWithEmptyWorkspace();
|
||||
tree.write('settings.gradle', '');
|
||||
});
|
||||
|
||||
it('should add the plugin', async () => {
|
||||
await initGenerator(tree, {
|
||||
skipFormat: true,
|
||||
skipPackageJson: false,
|
||||
});
|
||||
const nxJson = readNxJson(tree);
|
||||
expect(nxJson.plugins).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"options": {
|
||||
"buildTargetName": "build",
|
||||
"classesTargetName": "classes",
|
||||
"testTargetName": "test",
|
||||
},
|
||||
"plugin": "@nx/gradle/plugin",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('should not overwrite existing plugins', async () => {
|
||||
updateNxJson(tree, {
|
||||
plugins: ['foo'],
|
||||
});
|
||||
await initGenerator(tree, {
|
||||
skipFormat: true,
|
||||
skipPackageJson: false,
|
||||
});
|
||||
const nxJson = readNxJson(tree);
|
||||
expect(nxJson.plugins).toMatchInlineSnapshot(`
|
||||
[
|
||||
"foo",
|
||||
{
|
||||
"options": {
|
||||
"buildTargetName": "build",
|
||||
"classesTargetName": "classes",
|
||||
"testTargetName": "test",
|
||||
},
|
||||
"plugin": "@nx/gradle/plugin",
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
it('should not add plugin if already in array', async () => {
|
||||
updateNxJson(tree, {
|
||||
plugins: ['@nx/gradle/plugin'],
|
||||
});
|
||||
await initGenerator(tree, {
|
||||
skipFormat: true,
|
||||
skipPackageJson: false,
|
||||
});
|
||||
const nxJson = readNxJson(tree);
|
||||
expect(nxJson.plugins).toMatchInlineSnapshot(`
|
||||
[
|
||||
"@nx/gradle/plugin",
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
102
packages/gradle/src/generators/init/init.ts
Normal file
102
packages/gradle/src/generators/init/init.ts
Normal file
@ -0,0 +1,102 @@
|
||||
import {
|
||||
addDependenciesToPackageJson,
|
||||
formatFiles,
|
||||
GeneratorCallback,
|
||||
logger,
|
||||
readNxJson,
|
||||
runTasksInSerial,
|
||||
Tree,
|
||||
updateNxJson,
|
||||
} from '@nx/devkit';
|
||||
import { updatePackageScripts } from '@nx/devkit/src/utils/update-package-scripts';
|
||||
import { createNodes } from '../../plugin/nodes';
|
||||
import { nxVersion } from '../../utils/versions';
|
||||
import { InitGeneratorSchema } from './schema';
|
||||
import { hasGradlePlugin } from '../../utils/has-gradle-plugin';
|
||||
|
||||
export async function initGenerator(tree: Tree, options: InitGeneratorSchema) {
|
||||
const tasks: GeneratorCallback[] = [];
|
||||
|
||||
if (!options.skipPackageJson && tree.exists('package.json')) {
|
||||
tasks.push(
|
||||
addDependenciesToPackageJson(
|
||||
tree,
|
||||
{},
|
||||
{
|
||||
'@nx/gradle': nxVersion,
|
||||
},
|
||||
undefined,
|
||||
options.keepExistingVersions
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
addPlugin(tree);
|
||||
addProjectReportToBuildGradle(tree);
|
||||
|
||||
if (options.updatePackageScripts && tree.exists('package.json')) {
|
||||
await updatePackageScripts(tree, createNodes);
|
||||
}
|
||||
|
||||
if (!options.skipFormat) {
|
||||
await formatFiles(tree);
|
||||
}
|
||||
|
||||
return runTasksInSerial(...tasks);
|
||||
}
|
||||
|
||||
function addPlugin(tree: Tree) {
|
||||
const nxJson = readNxJson(tree);
|
||||
|
||||
if (!hasGradlePlugin(tree)) {
|
||||
nxJson.plugins ??= [];
|
||||
nxJson.plugins.push({
|
||||
plugin: '@nx/gradle/plugin',
|
||||
options: {
|
||||
testTargetName: 'test',
|
||||
classesTargetName: 'classes',
|
||||
buildTargetName: 'build',
|
||||
},
|
||||
});
|
||||
updateNxJson(tree, nxJson);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function adds the project-report plugin to the build.gradle or build.gradle.kts file
|
||||
*/
|
||||
function addProjectReportToBuildGradle(tree: Tree) {
|
||||
let buildGradleFile: string;
|
||||
if (tree.exists('settings.gradle.kts')) {
|
||||
buildGradleFile = 'build.gradle.kts';
|
||||
} else if (tree.exists('settings.gradle')) {
|
||||
buildGradleFile = 'build.gradle';
|
||||
} else {
|
||||
throw new Error(
|
||||
'Could not find settings.gradle or settings.gradle.kts file in your gradle workspace.'
|
||||
);
|
||||
}
|
||||
let buildGradleContent = '';
|
||||
if (tree.exists(buildGradleFile)) {
|
||||
buildGradleContent = tree.read(buildGradleFile).toString();
|
||||
}
|
||||
if (buildGradleContent.includes('allprojects')) {
|
||||
if (!buildGradleContent.includes('"project-report')) {
|
||||
logger.warn(`Please add the project-report plugin to your ${buildGradleFile}:
|
||||
allprojects {
|
||||
apply {
|
||||
plugin("project-report")
|
||||
}
|
||||
}`);
|
||||
}
|
||||
} else {
|
||||
buildGradleContent += `\n\rallprojects {
|
||||
apply {
|
||||
plugin("project-report")
|
||||
}
|
||||
}`;
|
||||
tree.write(buildGradleFile, buildGradleContent);
|
||||
}
|
||||
}
|
||||
|
||||
export default initGenerator;
|
||||
6
packages/gradle/src/generators/init/schema.d.ts
vendored
Normal file
6
packages/gradle/src/generators/init/schema.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
export interface InitGeneratorSchema {
|
||||
skipFormat?: boolean;
|
||||
skipPackageJson?: boolean;
|
||||
keepExistingVersions?: boolean;
|
||||
updatePackageScripts?: boolean;
|
||||
}
|
||||
34
packages/gradle/src/generators/init/schema.json
Normal file
34
packages/gradle/src/generators/init/schema.json
Normal file
@ -0,0 +1,34 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/schema",
|
||||
"$id": "NxGradleInitSchema",
|
||||
"title": "Gradle Init Generator",
|
||||
"description": "Initializes a Gradle project in the current workspace.",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"skipFormat": {
|
||||
"description": "Skip formatting files.",
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"x-priority": "internal"
|
||||
},
|
||||
"skipPackageJson": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "Do not add dependencies to `package.json`.",
|
||||
"x-priority": "internal"
|
||||
},
|
||||
"keepExistingVersions": {
|
||||
"type": "boolean",
|
||||
"x-priority": "internal",
|
||||
"description": "Keep existing dependencies versions",
|
||||
"default": false
|
||||
},
|
||||
"updatePackageScripts": {
|
||||
"type": "boolean",
|
||||
"x-priority": "internal",
|
||||
"description": "Update `package.json` scripts with inferred targets",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
"required": []
|
||||
}
|
||||
@ -39,6 +39,7 @@ export const createDependencies: CreateDependencies = async (
|
||||
|
||||
if (projectName && depsFile) {
|
||||
dependencies = dependencies.concat(
|
||||
Array.from(
|
||||
processGradleDependencies(
|
||||
depsFile,
|
||||
gradleProjectToProjectName,
|
||||
@ -46,6 +47,7 @@ export const createDependencies: CreateDependencies = async (
|
||||
gradleFile,
|
||||
context
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -85,12 +87,15 @@ function processGradleDependencies(
|
||||
sourceProjectName: string,
|
||||
gradleFile: string,
|
||||
context: CreateDependenciesContext
|
||||
) {
|
||||
const dependencies: RawProjectGraphDependency[] = [];
|
||||
): Set<RawProjectGraphDependency> {
|
||||
const dependencies: Set<RawProjectGraphDependency> = new Set();
|
||||
const lines = readFileSync(depsFile).toString().split('\n');
|
||||
let inDeps = false;
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('implementationDependenciesMetadata')) {
|
||||
if (
|
||||
line.startsWith('implementationDependenciesMetadata') ||
|
||||
line.startsWith('compileClasspath')
|
||||
) {
|
||||
inDeps = true;
|
||||
continue;
|
||||
}
|
||||
@ -116,7 +121,7 @@ function processGradleDependencies(
|
||||
sourceFile: gradleFile,
|
||||
};
|
||||
validateDependency(dependency, context);
|
||||
dependencies.push(dependency);
|
||||
dependencies.add(dependency);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
packages/gradle/src/utils/has-gradle-plugin.ts
Normal file
10
packages/gradle/src/utils/has-gradle-plugin.ts
Normal file
@ -0,0 +1,10 @@
|
||||
import { readNxJson, Tree } from '@nx/devkit';
|
||||
|
||||
export function hasGradlePlugin(tree: Tree): boolean {
|
||||
const nxJson = readNxJson(tree);
|
||||
return !!nxJson.plugins?.some((p) =>
|
||||
typeof p === 'string'
|
||||
? p === '@nx/gradle/plugin'
|
||||
: p.plugin === '@nx/gradle/plugin'
|
||||
);
|
||||
}
|
||||
1
packages/gradle/src/utils/versions.ts
Normal file
1
packages/gradle/src/utils/versions.ts
Normal file
@ -0,0 +1 @@
|
||||
export const nxVersion = require('../../package.json').version;
|
||||
@ -63,7 +63,7 @@ async function installPackage(pkgName: string, version: string): Promise<void> {
|
||||
writeJsonFile('nx.json', nxJson);
|
||||
|
||||
try {
|
||||
await runNxAsync('');
|
||||
await runNxAsync('--help');
|
||||
} catch (e) {
|
||||
// revert adding the plugin to nx.json
|
||||
nxJson.installation.plugins[pkgName] = undefined;
|
||||
|
||||
@ -49,6 +49,13 @@ export async function initHandler(options: InitArgs): Promise<void> {
|
||||
);
|
||||
}
|
||||
generateDotNxSetup(version);
|
||||
const { plugins } = await detectPlugins();
|
||||
plugins.forEach((plugin) => {
|
||||
execSync(`./nx add ${plugin}`, {
|
||||
stdio: 'inherit',
|
||||
});
|
||||
});
|
||||
|
||||
// invokes the wrapper, thus invoking the initial installation process
|
||||
runNxSync('--version', { stdio: 'ignore' });
|
||||
return;
|
||||
@ -190,6 +197,9 @@ async function detectPlugins(): Promise<
|
||||
}
|
||||
}
|
||||
}
|
||||
if (existsSync('gradlew') || existsSync('gradlew.bat')) {
|
||||
detectedPlugins.add('@nx/gradle');
|
||||
}
|
||||
|
||||
const plugins = Array.from(detectedPlugins);
|
||||
|
||||
@ -214,7 +224,9 @@ async function detectPlugins(): Promise<
|
||||
|
||||
if (pluginsToInstall?.length === 0) return undefined;
|
||||
|
||||
const updatePackageScripts = await prompt<{ updatePackageScripts: string }>([
|
||||
const updatePackageScripts =
|
||||
existsSync('package.json') &&
|
||||
(await prompt<{ updatePackageScripts: string }>([
|
||||
{
|
||||
name: 'updatePackageScripts',
|
||||
type: 'autocomplete',
|
||||
@ -229,10 +241,7 @@ async function detectPlugins(): Promise<
|
||||
],
|
||||
initial: 0,
|
||||
},
|
||||
]).then((r) => r.updatePackageScripts === 'Yes');
|
||||
]).then((r) => r.updatePackageScripts === 'Yes'));
|
||||
|
||||
return {
|
||||
plugins: pluginsToInstall,
|
||||
updatePackageScripts,
|
||||
};
|
||||
return { plugins: pluginsToInstall, updatePackageScripts };
|
||||
}
|
||||
|
||||
@ -29,6 +29,7 @@ const scopes = [
|
||||
{ value: 'vue', name: 'vue: anything Vue specific' },
|
||||
{ value: 'web', name: 'web: anything Web specific' },
|
||||
{ value: 'webpack', name: 'webpack: anything Webpack specific' },
|
||||
{ value: 'gradle', name: 'gradle: anything Gradle specific'},
|
||||
{value: 'module-federation', name: 'module-federation: anything Module Federation specific'},
|
||||
];
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user