Skip to content

Commit 1435eef

Browse files
feat: build transient native dependencies (#5643)
Co-authored-by: Igor Randjelovic <[email protected]>
1 parent 356630a commit 1435eef

12 files changed

+325
-133
lines changed

lib/definitions/android-plugin-migrator.d.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,9 @@ interface IBuildAndroidPluginData extends Partial<IProjectDir> {
4444
* Optional custom Gradle path.
4545
*/
4646
gradlePath?: string;
47-
48-
/**
47+
48+
/**
4949
* Optional custom Gradle arguments.
5050
*/
51-
gradleArgs?: string,
51+
gradleArgs?: string;
5252
}

lib/services/android-plugin-build-service.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,8 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
258258
await this.setupGradle(
259259
pluginTempDir,
260260
options.platformsAndroidDirPath,
261-
options.projectDir
261+
options.projectDir,
262+
options.pluginName
262263
);
263264
await this.buildPlugin({
264265
gradlePath: options.gradlePath,
@@ -398,7 +399,8 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
398399
private async setupGradle(
399400
pluginTempDir: string,
400401
platformsAndroidDirPath: string,
401-
projectDir: string
402+
projectDir: string,
403+
pluginName: string
402404
): Promise<void> {
403405
const gradleTemplatePath = path.resolve(
404406
path.join(__dirname, "../../vendor/gradle-plugin")
@@ -419,6 +421,7 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
419421
buildGradlePath,
420422
runtimeGradleVersions.gradleAndroidPluginVersion
421423
);
424+
this.replaceFileContent(buildGradlePath, "{{pluginName}}", pluginName);
422425
}
423426

424427
private async getRuntimeGradleVersions(
@@ -729,7 +732,7 @@ export class AndroidPluginBuildService implements IAndroidPluginBuildService {
729732
`-PcompileSdk=android-${pluginBuildSettings.androidToolsInfo.compileSdkVersion}`,
730733
`-PbuildToolsVersion=${pluginBuildSettings.androidToolsInfo.buildToolsVersion}`,
731734
`-PappPath=${this.$projectData.getAppDirectoryPath()}`,
732-
`-PappResourcesPath=${this.$projectData.getAppResourcesDirectoryPath()}`
735+
`-PappResourcesPath=${this.$projectData.getAppResourcesDirectoryPath()}`,
733736
];
734737
if (pluginBuildSettings.gradleArgs) {
735738
localArgs.push(pluginBuildSettings.gradleArgs);

lib/services/android-project-service.ts

+97-10
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,71 @@ import { IInjector } from "../common/definitions/yok";
4848
import { injector } from "../common/yok";
4949
import { INotConfiguredEnvOptions } from "../common/definitions/commands";
5050

51+
interface NativeDependency {
52+
name: string;
53+
directory: string;
54+
dependencies: string[];
55+
}
56+
57+
//
58+
// we sort the native dependencies topologically to make sure they are processed in the right order
59+
// native dependenciess need to be sorted so the deepst dependencies are built before it's parents
60+
//
61+
// for example, given this dep structure (assuming these are all native dependencies that need to be built)
62+
// |- dep1
63+
// |- dep2
64+
// |- dep3
65+
// |- dep4
66+
// |-dep5
67+
// |- dep6
68+
//
69+
// It is sorted:
70+
// |- dep1 - doesn't depend on anything, so the order stays the same as in the input list
71+
// |- dep3 - doesn't depend on anything, so the order stays the same as in the input list
72+
// |- dep5 - doesn't depend on anything, so the order stays the same as in the input list
73+
// |- dep6 - doesn't depend on anything, so the order stays the same as in the input list
74+
// |- dep4 - depends on dep6, so dep6 must be built first, ie above ^
75+
// |- dep2 - depends on dep3, dep4, dep5 and dep6, so all of them must be built first
76+
//
77+
// for more details see: https://wikiless.org/wiki/Topological_sorting?lang=en
78+
//
79+
function topologicalSortNativeDependencies(
80+
nativeDeps: NativeDependency[],
81+
start: NativeDependency[] = [],
82+
depth = 0
83+
): NativeDependency[] {
84+
const processedDeps = nativeDeps.reduce(
85+
(accumulator, nativeDep: NativeDependency) => {
86+
if (
87+
nativeDep.dependencies.every(
88+
Array.prototype.includes,
89+
accumulator.map((n) => n.name)
90+
)
91+
) {
92+
accumulator.push(nativeDep);
93+
}
94+
return accumulator;
95+
},
96+
start
97+
);
98+
99+
const remainingDeps = nativeDeps.filter(
100+
(nativeDep) => !processedDeps.includes(nativeDep)
101+
);
102+
103+
// recurse if we still have unprocessed deps
104+
// the second condition here prevents infinite recursion
105+
if (remainingDeps.length && depth <= nativeDeps.length) {
106+
return topologicalSortNativeDependencies(
107+
remainingDeps,
108+
processedDeps,
109+
depth + 1
110+
);
111+
}
112+
113+
return processedDeps;
114+
}
115+
51116
export class AndroidProjectService extends projectServiceBaseLib.PlatformProjectServiceBase {
52117
private static VALUES_DIRNAME = "values";
53118
private static VALUES_VERSION_DIRNAME_PREFIX =
@@ -635,10 +700,10 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
635700
public async beforePrepareAllPlugins(
636701
projectData: IProjectData,
637702
dependencies?: IDependencyData[]
638-
): Promise<void> {
703+
): Promise<IDependencyData[]> {
639704
if (dependencies) {
640705
dependencies = this.filterUniqueDependencies(dependencies);
641-
this.provideDependenciesJson(projectData, dependencies);
706+
return this.provideDependenciesJson(projectData, dependencies);
642707
}
643708
}
644709

@@ -666,7 +731,7 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
666731
private provideDependenciesJson(
667732
projectData: IProjectData,
668733
dependencies: IDependencyData[]
669-
): void {
734+
): IDependencyData[] {
670735
const platformDir = path.join(
671736
projectData.platformsDir,
672737
AndroidProjectService.ANDROID_PLATFORM_NAME
@@ -675,15 +740,37 @@ export class AndroidProjectService extends projectServiceBaseLib.PlatformProject
675740
platformDir,
676741
constants.DEPENDENCIES_JSON_NAME
677742
);
678-
const nativeDependencies = dependencies
679-
.filter(AndroidProjectService.isNativeAndroidDependency)
680-
.map(({ name, directory }) => ({
681-
name,
682-
directory: path.relative(platformDir, directory),
683-
}));
684-
const jsonContent = JSON.stringify(nativeDependencies, null, 4);
743+
let nativeDependencyData = dependencies.filter(
744+
AndroidProjectService.isNativeAndroidDependency
745+
);
685746

747+
let nativeDependencies = nativeDependencyData.map(
748+
({ name, directory, dependencies }) => {
749+
return {
750+
name,
751+
directory: path.relative(platformDir, directory),
752+
dependencies: dependencies.filter((dep) => {
753+
// filter out transient dependencies that don't have native dependencies
754+
return (
755+
nativeDependencyData.findIndex(
756+
(nativeDep) => nativeDep.name === dep
757+
) !== -1
758+
);
759+
}),
760+
} as NativeDependency;
761+
}
762+
);
763+
nativeDependencies = topologicalSortNativeDependencies(nativeDependencies);
764+
const jsonContent = JSON.stringify(nativeDependencies, null, 4);
686765
this.$fs.writeFile(dependenciesJsonPath, jsonContent);
766+
767+
// we sort all the dependencies to respect the topological sorting of the native dependencies
768+
return dependencies.sort(function (a, b) {
769+
return (
770+
nativeDependencies.findIndex((n) => n.name === a.name) -
771+
nativeDependencies.findIndex((n) => n.name === b.name)
772+
);
773+
});
687774
}
688775

689776
private static isNativeAndroidDependency({

lib/services/ios-project-service.ts

+17-4
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ import { IOSProvisionService } from "./ios-provision-service";
1313
import { IOSEntitlementsService } from "./ios-entitlements-service";
1414
import { IOSBuildData } from "../data/build-data";
1515
import { IOSPrepareData } from "../data/prepare-data";
16-
import { BUILD_XCCONFIG_FILE_NAME, CONFIG_FILE_NAME_DISPLAY, IosProjectConstants } from "../constants";
16+
import {
17+
BUILD_XCCONFIG_FILE_NAME,
18+
CONFIG_FILE_NAME_DISPLAY,
19+
IosProjectConstants,
20+
} from "../constants";
1721
import { hook } from "../common/helpers";
1822
import {
1923
IPlatformData,
@@ -29,8 +33,14 @@ import {
2933
IIOSNativeTargetService,
3034
IValidatePlatformOutput,
3135
} from "../definitions/project";
36+
3237
import { IBuildData } from "../definitions/build";
33-
import { IXcprojService, IXcconfigService, IOptions } from "../declarations";
38+
import {
39+
IXcprojService,
40+
IXcconfigService,
41+
IDependencyData,
42+
IOptions,
43+
} from "../declarations";
3444
import { IPluginData, IPluginsService } from "../definitions/plugins";
3545
import {
3646
IFileSystem,
@@ -976,8 +986,11 @@ export class IOSProjectService extends projectServiceBaseLib.PlatformProjectServ
976986
await this.addExtensions(projectData, pluginsData);
977987
}
978988

979-
public beforePrepareAllPlugins(): Promise<void> {
980-
return Promise.resolve();
989+
public beforePrepareAllPlugins(
990+
projectData: IProjectData,
991+
dependencies?: IDependencyData[]
992+
): Promise<IDependencyData[]> {
993+
return Promise.resolve(dependencies);
981994
}
982995

983996
public async checkForChanges(

lib/services/platform-environment-requirements.ts

+2-3
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,8 @@ export class PlatformEnvironmentRequirements
2424
// private $staticConfig: IStaticConfig,
2525
private $analyticsService: IAnalyticsService,
2626
// @ts-ignore - required by the hook helper!
27-
private $injector: IInjector
28-
) // private $previewQrCodeService: IPreviewQrCodeService
29-
{}
27+
private $injector: IInjector // private $previewQrCodeService: IPreviewQrCodeService
28+
) {}
3029

3130
// public get $previewAppController(): IPreviewAppController {
3231
// return this.$injector.resolve("previewAppController");

lib/services/project-changes-service.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,10 @@ export class ProjectChangesService implements IProjectChangesService {
9393
);
9494

9595
this.$nodeModulesDependenciesBuilder
96-
.getProductionDependencies(projectData.projectDir, projectData.ignoredDependencies)
96+
.getProductionDependencies(
97+
projectData.projectDir,
98+
projectData.ignoredDependencies
99+
)
97100
.filter(
98101
(dep) =>
99102
dep.nativescript &&

lib/services/webpack/webpack.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,7 @@ declare global {
163163
beforePrepareAllPlugins(
164164
projectData: IProjectData,
165165
dependencies?: IDependencyData[]
166-
): Promise<void>;
166+
): Promise<IDependencyData[]>;
167167

168168
handleNativeDependenciesChange(
169169
projectData: IProjectData,

lib/tools/node-modules/node-modules-builder.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,10 @@ export class NodeModulesBuilder implements INodeModulesBuilder {
1818
platformData,
1919
projectData,
2020
}: IPrepareNodeModulesData): Promise<void> {
21-
const dependencies = this.$nodeModulesDependenciesBuilder.getProductionDependencies(
21+
let dependencies = this.$nodeModulesDependenciesBuilder.getProductionDependencies(
2222
projectData.projectDir, projectData.ignoredDependencies
2323
);
24-
await platformData.platformProjectService.beforePrepareAllPlugins(
24+
dependencies = await platformData.platformProjectService.beforePrepareAllPlugins(
2525
projectData,
2626
dependencies
2727
);

lib/tools/node-modules/node-modules-dependencies-builder.ts

+22-7
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { IDependencyData } from "../../declarations";
55
import { IFileSystem } from "../../common/declarations";
66
import * as _ from "lodash";
77
import { injector } from "../../common/yok";
8+
import { resolvePackagePath } from "@rigor789/resolve-package-path";
89

910
interface IDependencyDescription {
1011
parent: IDependencyDescription;
@@ -43,7 +44,8 @@ export class NodeModulesDependenciesBuilder
4344
const currentModule = queue.shift();
4445
const resolvedDependency = this.findModule(
4546
currentModule,
46-
resolvedDependencies
47+
resolvedDependencies,
48+
projectPath
4749
);
4850

4951
if (
@@ -86,16 +88,29 @@ export class NodeModulesDependenciesBuilder
8688

8789
private findModule(
8890
depDescription: IDependencyDescription,
89-
resolvedDependencies: IDependencyData[]
91+
resolvedDependencies: IDependencyData[],
92+
rootPath: string
9093
): IDependencyData {
9194
try {
9295
const parentModulesPath =
9396
depDescription?.parentDir ?? depDescription?.parent?.parentDir;
94-
const modulePath = require
95-
.resolve(`${depDescription.name}/package.json`, {
96-
paths: [parentModulesPath],
97-
})
98-
.replace(/[\\/]+package\.json$/, "");
97+
98+
let modulePath: string = resolvePackagePath(depDescription.name, {
99+
paths: [parentModulesPath],
100+
});
101+
102+
// perhaps traverse up the tree here?
103+
if (!modulePath) {
104+
// fallback to searching in the root path
105+
modulePath = resolvePackagePath(depDescription.name, {
106+
paths: [rootPath],
107+
});
108+
}
109+
110+
// if we failed to find the module...
111+
if (!modulePath) {
112+
return null;
113+
}
99114

100115
// if we already resolved this dependency, we return null to avoid a duplicate resolution
101116
if (

0 commit comments

Comments
 (0)