fix(gradle): add gradle migration to change ciTargetName (#30965)
<!-- 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 --> this pr address comments in https://github.com/nrwl/nx/pull/30457 ## Expected Behavior <!-- This is the behavior we should expect with the changes in this PR --> - update executor description - use pseudo terminal when run batch command - add an e2e test to run gradle command in batch - add migration script to change ciTargetName to ciTestTargetName ## Related Issue(s) <!-- Please link the issue being fixed so it gets closed when this is merged. --> Fixes #
This commit is contained in:
parent
11ca6fc685
commit
3877a43a47
@ -2105,7 +2105,7 @@
|
|||||||
"source": "/packages/gradle/src",
|
"source": "/packages/gradle/src",
|
||||||
"executors": {
|
"executors": {
|
||||||
"/nx-api/gradle/executors/gradle": {
|
"/nx-api/gradle/executors/gradle": {
|
||||||
"description": "The Gradlew executor is used to run Gradle tasks.",
|
"description": "Runs gradle tasks via the Gradle Tooling API or by invoking gradlew.",
|
||||||
"file": "generated/packages/gradle/executors/gradle.json",
|
"file": "generated/packages/gradle/executors/gradle.json",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"name": "gradle",
|
"name": "gradle",
|
||||||
@ -2135,6 +2135,16 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"migrations": {
|
"migrations": {
|
||||||
|
"/nx-api/gradle/migrations/change-ciTargetName-to-ciTestTargetName": {
|
||||||
|
"description": "Change @nx/gradle option from ciTargetName to ciTestTargetName",
|
||||||
|
"file": "generated/packages/gradle/migrations/change-ciTargetName-to-ciTestTargetName.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "change-ciTargetName-to-ciTestTargetName",
|
||||||
|
"version": "21.0.0-beta.13",
|
||||||
|
"originalFilePath": "/packages/gradle",
|
||||||
|
"path": "/nx-api/gradle/migrations/change-ciTargetName-to-ciTestTargetName",
|
||||||
|
"type": "migration"
|
||||||
|
},
|
||||||
"/nx-api/gradle/migrations/change-plugin-to-v1": {
|
"/nx-api/gradle/migrations/change-plugin-to-v1": {
|
||||||
"description": "Change @nx/gradle plugin to version 1",
|
"description": "Change @nx/gradle plugin to version 1",
|
||||||
"file": "generated/packages/gradle/migrations/change-plugin-to-v1.json",
|
"file": "generated/packages/gradle/migrations/change-plugin-to-v1.json",
|
||||||
|
|||||||
@ -2089,7 +2089,7 @@
|
|||||||
],
|
],
|
||||||
"executors": [
|
"executors": [
|
||||||
{
|
{
|
||||||
"description": "The Gradlew executor is used to run Gradle tasks.",
|
"description": "Runs gradle tasks via the Gradle Tooling API or by invoking gradlew.",
|
||||||
"file": "generated/packages/gradle/executors/gradle.json",
|
"file": "generated/packages/gradle/executors/gradle.json",
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"name": "gradle",
|
"name": "gradle",
|
||||||
@ -2119,6 +2119,16 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"migrations": [
|
"migrations": [
|
||||||
|
{
|
||||||
|
"description": "Change @nx/gradle option from ciTargetName to ciTestTargetName",
|
||||||
|
"file": "generated/packages/gradle/migrations/change-ciTargetName-to-ciTestTargetName.json",
|
||||||
|
"hidden": false,
|
||||||
|
"name": "change-ciTargetName-to-ciTestTargetName",
|
||||||
|
"version": "21.0.0-beta.13",
|
||||||
|
"originalFilePath": "/packages/gradle",
|
||||||
|
"path": "gradle/migrations/change-ciTargetName-to-ciTestTargetName",
|
||||||
|
"type": "migration"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"description": "Change @nx/gradle plugin to version 1",
|
"description": "Change @nx/gradle plugin to version 1",
|
||||||
"file": "generated/packages/gradle/migrations/change-plugin-to-v1.json",
|
"file": "generated/packages/gradle/migrations/change-plugin-to-v1.json",
|
||||||
|
|||||||
@ -29,7 +29,7 @@
|
|||||||
"required": ["taskName"],
|
"required": ["taskName"],
|
||||||
"presets": []
|
"presets": []
|
||||||
},
|
},
|
||||||
"description": "The Gradlew executor is used to run Gradle tasks.",
|
"description": "Runs gradle tasks via the Gradle Tooling API or by invoking gradlew.",
|
||||||
"aliases": [],
|
"aliases": [],
|
||||||
"hidden": false,
|
"hidden": false,
|
||||||
"path": "/packages/gradle/src/executors/gradle/schema.json",
|
"path": "/packages/gradle/src/executors/gradle/schema.json",
|
||||||
|
|||||||
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"name": "change-ciTargetName-to-ciTestTargetName",
|
||||||
|
"version": "21.0.0-beta.13",
|
||||||
|
"cli": "nx",
|
||||||
|
"description": "Change @nx/gradle option from ciTargetName to ciTestTargetName",
|
||||||
|
"factory": "./src/migrations/21-0-0/change-ciTargetName-to-ciTestTargetName",
|
||||||
|
"implementation": "/packages/gradle/src/migrations/21-0-0/change-ciTargetName-to-ciTestTargetName.ts",
|
||||||
|
"aliases": [],
|
||||||
|
"hidden": false,
|
||||||
|
"path": "/packages/gradle",
|
||||||
|
"schema": null,
|
||||||
|
"type": "migration",
|
||||||
|
"examplesFile": "#### Change @nx/gradle plugin option ciTargetName to ciTestTargetName\n\nChange @nx/gradle plugin option ciTargetName to ciTestTargetName in nx.json\n\n#### Sample Code Changes\n\n{% tabs %}\n{% tab label=\"Before\" %}\n\n```json {% fileName=\"nx.json\" %}\n{\n \"plugins\": [\n \"plugin\": \"@nx/gradle\",\n \"options\": {\n \"ciTargetName\": \"ci\"\n }\n ]\n}\n```\n\n{% /tab %}\n{% tab label=\"After\" %}\n\n```json {% highlightLines=[5] fileName=\"nx.json\" %}\n{\n \"plugins\": [\n \"plugin\": \"@nx/gradle\",\n \"options\": {\n \"ciTestTargetName\": \"ci\"\n }\n ]\n}\n```\n\n{% /tab %}\n{% /tabs %}\n"
|
||||||
|
}
|
||||||
@ -30,7 +30,7 @@ describe('Gradle', () => {
|
|||||||
expect(projects).toContain('utilities');
|
expect(projects).toContain('utilities');
|
||||||
expect(projects).toContain(gradleProjectName);
|
expect(projects).toContain(gradleProjectName);
|
||||||
|
|
||||||
const buildOutput = runCLI('build app', { verbose: true });
|
let buildOutput = runCLI('build app', { verbose: true });
|
||||||
expect(buildOutput).toContain(':list:classes');
|
expect(buildOutput).toContain(':list:classes');
|
||||||
expect(buildOutput).toContain(':utilities:classes');
|
expect(buildOutput).toContain(':utilities:classes');
|
||||||
|
|
||||||
@ -39,6 +39,10 @@ describe('Gradle', () => {
|
|||||||
`list/build/libs/list.jar`,
|
`list/build/libs/list.jar`,
|
||||||
`utilities/build/libs/utilities.jar`
|
`utilities/build/libs/utilities.jar`
|
||||||
);
|
);
|
||||||
|
|
||||||
|
buildOutput = runCLI('build app --batch', { verbose: true });
|
||||||
|
expect(buildOutput).toContain(':list:classes');
|
||||||
|
expect(buildOutput).toContain(':utilities:classes');
|
||||||
});
|
});
|
||||||
|
|
||||||
xit('should track dependencies for new app', () => {
|
xit('should track dependencies for new app', () => {
|
||||||
@ -86,6 +90,11 @@ dependencies {
|
|||||||
expect(buildOutput).toContain(':utilities:classes');
|
expect(buildOutput).toContain(':utilities:classes');
|
||||||
|
|
||||||
checkFilesExist(`app2/build/libs/app2.jar`);
|
checkFilesExist(`app2/build/libs/app2.jar`);
|
||||||
|
|
||||||
|
buildOutput = runCLI('build app2 --batch', { verbose: true });
|
||||||
|
expect(buildOutput).toContain(':app:classes');
|
||||||
|
expect(buildOutput).toContain(':list:classes');
|
||||||
|
expect(buildOutput).toContain(':utilities:classes');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should run atomized test target', () => {
|
it('should run atomized test target', () => {
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
"batchImplementation": "./src/executors/gradle/gradle-batch.impl",
|
"batchImplementation": "./src/executors/gradle/gradle-batch.impl",
|
||||||
"implementation": "./src/executors/gradle/gradle.impl",
|
"implementation": "./src/executors/gradle/gradle.impl",
|
||||||
"schema": "./src/executors/gradle/schema.json",
|
"schema": "./src/executors/gradle/schema.json",
|
||||||
"description": "The Gradlew executor is used to run Gradle tasks."
|
"description": "Runs gradle tasks via the Gradle Tooling API or by invoking gradlew."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,6 +23,12 @@
|
|||||||
"cli": "nx",
|
"cli": "nx",
|
||||||
"description": "Change @nx/gradle plugin to version 1",
|
"description": "Change @nx/gradle plugin to version 1",
|
||||||
"factory": "./src/migrations/21-0-0/change-plugin-to-v1"
|
"factory": "./src/migrations/21-0-0/change-plugin-to-v1"
|
||||||
|
},
|
||||||
|
"change-ciTargetName-to-ciTestTargetName": {
|
||||||
|
"version": "21.0.0-beta.13",
|
||||||
|
"cli": "nx",
|
||||||
|
"description": "Change @nx/gradle option from ciTargetName to ciTestTargetName",
|
||||||
|
"factory": "./src/migrations/21-0-0/change-ciTargetName-to-ciTestTargetName"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"packageJsonUpdates": {}
|
"packageJsonUpdates": {}
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
import { ExecutorContext, output, TaskGraph } from '@nx/devkit';
|
import { ExecutorContext, output, TaskGraph, workspaceRoot } from '@nx/devkit';
|
||||||
import {
|
import {
|
||||||
LARGE_BUFFER,
|
LARGE_BUFFER,
|
||||||
RunCommandsOptions,
|
RunCommandsOptions,
|
||||||
@ -8,6 +8,10 @@ import { gradleExecutorSchema } from './schema';
|
|||||||
import { findGradlewFile } from '../../utils/exec-gradle';
|
import { findGradlewFile } from '../../utils/exec-gradle';
|
||||||
import { dirname, join } from 'path';
|
import { dirname, join } from 'path';
|
||||||
import { execSync } from 'child_process';
|
import { execSync } from 'child_process';
|
||||||
|
import {
|
||||||
|
createPseudoTerminal,
|
||||||
|
PseudoTerminal,
|
||||||
|
} from 'nx/src/tasks-runner/pseudo-terminal';
|
||||||
|
|
||||||
export const batchRunnerPath = join(
|
export const batchRunnerPath = join(
|
||||||
__dirname,
|
__dirname,
|
||||||
@ -56,20 +60,42 @@ export default async function gradleBatch(
|
|||||||
return gradlewTasksToRun;
|
return gradlewTasksToRun;
|
||||||
}, {});
|
}, {});
|
||||||
const gradlewBatchStart = performance.mark(`gradlew-batch:start`);
|
const gradlewBatchStart = performance.mark(`gradlew-batch:start`);
|
||||||
const batchResults = execSync(
|
|
||||||
`java -jar ${batchRunnerPath} --tasks='${JSON.stringify(
|
const usePseudoTerminal =
|
||||||
gradlewTasksToRun
|
process.env.NX_NATIVE_COMMAND_RUNNER !== 'false' &&
|
||||||
)}' --workspaceRoot=${root} --args='${args
|
PseudoTerminal.isSupported();
|
||||||
.join(' ')
|
const command = `java -jar ${batchRunnerPath} --tasks='${JSON.stringify(
|
||||||
.replaceAll("'", '"')}' ${
|
gradlewTasksToRun
|
||||||
process.env.NX_VERBOSE_LOGGING === 'true' ? '' : '--quiet'
|
)}' --workspaceRoot=${root} --args='${args
|
||||||
}`,
|
.join(' ')
|
||||||
{
|
.replaceAll("'", '"')}' ${
|
||||||
|
process.env.NX_VERBOSE_LOGGING === 'true' ? '' : '--quiet'
|
||||||
|
}`;
|
||||||
|
let batchResults;
|
||||||
|
if (usePseudoTerminal) {
|
||||||
|
const terminal = createPseudoTerminal();
|
||||||
|
await terminal.init();
|
||||||
|
|
||||||
|
const cp = terminal.runCommand(command, {
|
||||||
|
cwd: workspaceRoot,
|
||||||
|
jsEnv: process.env,
|
||||||
|
quiet: process.env.NX_VERBOSE_LOGGING !== 'true',
|
||||||
|
});
|
||||||
|
const results = await cp.getResults();
|
||||||
|
batchResults = results.terminalOutput;
|
||||||
|
|
||||||
|
batchResults = batchResults.replace(command, '');
|
||||||
|
const startIndex = batchResults.indexOf('{');
|
||||||
|
const endIndex = batchResults.lastIndexOf('}');
|
||||||
|
batchResults = batchResults.substring(startIndex, endIndex + 1);
|
||||||
|
} else {
|
||||||
|
batchResults = execSync(command, {
|
||||||
|
cwd: workspaceRoot,
|
||||||
windowsHide: true,
|
windowsHide: true,
|
||||||
env: process.env,
|
env: process.env,
|
||||||
maxBuffer: LARGE_BUFFER,
|
maxBuffer: LARGE_BUFFER,
|
||||||
}
|
}).toString();
|
||||||
);
|
}
|
||||||
const gradlewBatchEnd = performance.mark(`gradlew-batch:end`);
|
const gradlewBatchEnd = performance.mark(`gradlew-batch:end`);
|
||||||
performance.measure(
|
performance.measure(
|
||||||
`gradlew-batch`,
|
`gradlew-batch`,
|
||||||
|
|||||||
@ -0,0 +1,36 @@
|
|||||||
|
#### Change @nx/gradle plugin option ciTargetName to ciTestTargetName
|
||||||
|
|
||||||
|
Change @nx/gradle plugin option ciTargetName to ciTestTargetName in nx.json
|
||||||
|
|
||||||
|
#### Sample Code Changes
|
||||||
|
|
||||||
|
{% tabs %}
|
||||||
|
{% tab label="Before" %}
|
||||||
|
|
||||||
|
```json {% fileName="nx.json" %}
|
||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
"plugin": "@nx/gradle",
|
||||||
|
"options": {
|
||||||
|
"ciTargetName": "ci"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
{% /tab %}
|
||||||
|
{% tab label="After" %}
|
||||||
|
|
||||||
|
```json {% highlightLines=[5] fileName="nx.json" %}
|
||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
"plugin": "@nx/gradle",
|
||||||
|
"options": {
|
||||||
|
"ciTestTargetName": "ci"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
{% /tab %}
|
||||||
|
{% /tabs %}
|
||||||
@ -0,0 +1,94 @@
|
|||||||
|
import { Tree, readNxJson } from '@nx/devkit';
|
||||||
|
import { createTreeWithEmptyWorkspace } from '@nx/devkit/testing';
|
||||||
|
import update from './change-ciTargetName-to-ciTestTargetName';
|
||||||
|
|
||||||
|
describe('change ciTargetName to ciTestTargetName', () => {
|
||||||
|
let tree: Tree;
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
tree = createTreeWithEmptyWorkspace();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not add @nx/gradle plugin if it does not exist', async () => {
|
||||||
|
tree.write('nx.json', JSON.stringify({ namedInputs: {} }));
|
||||||
|
update(tree);
|
||||||
|
expect(readNxJson(tree)).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"namedInputs": {},
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not add options to @nx/gradle if it have any options', async () => {
|
||||||
|
tree.write('nx.json', JSON.stringify({ plugins: ['@nx/gradle'] }));
|
||||||
|
update(tree);
|
||||||
|
expect(readNxJson(tree)).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
"@nx/gradle",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not add options to @nx/gradle/plugin-v1', async () => {
|
||||||
|
tree.write(
|
||||||
|
'nx.json',
|
||||||
|
JSON.stringify({ plugins: ['@nx/gradle/plugin-v1'] })
|
||||||
|
);
|
||||||
|
update(tree);
|
||||||
|
expect(readNxJson(tree)).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
"@nx/gradle/plugin-v1",
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not change to @nx/gradle plugin if ciTargetName does not exist', async () => {
|
||||||
|
tree.write(
|
||||||
|
'nx.json',
|
||||||
|
JSON.stringify({
|
||||||
|
plugins: [
|
||||||
|
{ plugin: '@nx/gradle', options: { testTargetName: 'test' } },
|
||||||
|
],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
update(tree);
|
||||||
|
expect(readNxJson(tree)).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"options": {
|
||||||
|
"testTargetName": "test",
|
||||||
|
},
|
||||||
|
"plugin": "@nx/gradle",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should change to @nx/gradle plugin ciTargetName', async () => {
|
||||||
|
tree.write(
|
||||||
|
'nx.json',
|
||||||
|
JSON.stringify({
|
||||||
|
plugins: [{ plugin: '@nx/gradle', options: { ciTargetName: 'test' } }],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
update(tree);
|
||||||
|
expect(readNxJson(tree)).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"plugins": [
|
||||||
|
{
|
||||||
|
"options": {
|
||||||
|
"ciTestTargetName": "test",
|
||||||
|
},
|
||||||
|
"plugin": "@nx/gradle",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
import { Tree, readNxJson, updateNxJson } from '@nx/devkit';
|
||||||
|
import { hasGradlePlugin } from '../../utils/has-gradle-plugin';
|
||||||
|
|
||||||
|
/* This function changes the @nx/gradle plugin option from ciTargetName to ciTestTargetName
|
||||||
|
*/
|
||||||
|
export default function update(tree: Tree) {
|
||||||
|
const nxJson = readNxJson(tree);
|
||||||
|
if (!nxJson) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!hasGradlePlugin(tree)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let gradlePluginIndex = nxJson.plugins.findIndex((p) =>
|
||||||
|
typeof p === 'string' ? p === '@nx/gradle' : p.plugin === '@nx/gradle'
|
||||||
|
);
|
||||||
|
let gradlePlugin = nxJson.plugins[gradlePluginIndex];
|
||||||
|
if (
|
||||||
|
typeof gradlePlugin === 'object' &&
|
||||||
|
gradlePlugin.plugin === '@nx/gradle'
|
||||||
|
) {
|
||||||
|
const ciTargetName = (gradlePlugin.options as Record<string, string>)?.[
|
||||||
|
'ciTargetName'
|
||||||
|
];
|
||||||
|
if (ciTargetName) {
|
||||||
|
delete (gradlePlugin.options as Record<string, string>)?.['ciTargetName'];
|
||||||
|
(gradlePlugin.options as Record<string, string>)['ciTestTargetName'] =
|
||||||
|
ciTargetName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updateNxJson(tree, nxJson);
|
||||||
|
}
|
||||||
@ -31,7 +31,7 @@ describe('ChangePluginToV1', () => {
|
|||||||
`);
|
`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should add change to @nx/gradle plugin with options', async () => {
|
it('should change @nx/gradle plugin name with options', async () => {
|
||||||
tree.write(
|
tree.write(
|
||||||
'nx.json',
|
'nx.json',
|
||||||
JSON.stringify({
|
JSON.stringify({
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import {
|
|||||||
import { GradlePluginOptions } from './utils/gradle-plugin-options';
|
import { GradlePluginOptions } from './utils/gradle-plugin-options';
|
||||||
import { GRALDEW_FILES, splitConfigFiles } from '../utils/split-config-files';
|
import { GRALDEW_FILES, splitConfigFiles } from '../utils/split-config-files';
|
||||||
import { globWithWorkspaceContext } from 'nx/src/utils/workspace-context';
|
import { globWithWorkspaceContext } from 'nx/src/utils/workspace-context';
|
||||||
|
import { existsSync } from 'node:fs';
|
||||||
|
|
||||||
export const createDependencies: CreateDependencies<
|
export const createDependencies: CreateDependencies<
|
||||||
GradlePluginOptions
|
GradlePluginOptions
|
||||||
@ -52,7 +53,11 @@ export const createDependencies: CreateDependencies<
|
|||||||
Object.values(context.projects).find(
|
Object.values(context.projects).find(
|
||||||
(project) => target === project.root
|
(project) => target === project.root
|
||||||
)?.name ?? dependencyFromPlugin.target;
|
)?.name ?? dependencyFromPlugin.target;
|
||||||
if (!sourceProjectName || !targetProjectName) {
|
if (
|
||||||
|
!sourceProjectName ||
|
||||||
|
!targetProjectName ||
|
||||||
|
!existsSync(dependencyFromPlugin.sourceFile)
|
||||||
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const dependency: StaticDependency = {
|
const dependency: StaticDependency = {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user