Skip to content

feat: ability to embed NativeScript into host Swift and Kotlin projects #5803

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
98c7066
feat(ios): embedding into host Swift projects (#5769)
tdermendjiev May 28, 2024
bd8c4a0
feat(android): ability to embed into host Kotlin projects (#5776)
vmutafov May 28, 2024
6071589
chore: cleanup
NathanWalker May 28, 2024
18a9c6c
release: 8.8.0-embed.0
NathanWalker May 28, 2024
d231bc6
Merge branch 'main' into embed
NathanWalker Jun 28, 2024
a80a184
Merge remote-tracking branch 'origin/main' into embed
NathanWalker Jul 1, 2024
34948d9
refactor: rename and clean up embedding flags + add embed command/config
rigor789 Jul 2, 2024
d0f2033
feat: better config support
rigor789 Jul 2, 2024
c971cca
Merge remote-tracking branch 'origin/main' into embed
NathanWalker Jul 2, 2024
c79de71
release: 8.8.0-embed.1
NathanWalker Jul 2, 2024
a983a36
Merge remote-tracking branch 'origin/main' into embed
NathanWalker Jul 3, 2024
c4943c2
release: 8.8.0-embed.2
NathanWalker Jul 3, 2024
7061a7e
chore: wip embedding group
rigor789 Jul 5, 2024
1c40904
fix: reference embedded bundle files from root NativeScript folder in…
NathanWalker Jul 5, 2024
c7be511
chore: cleanup embed handling of build folder wip
NathanWalker Jul 6, 2024
9787469
chore: lock file
NathanWalker Jul 6, 2024
de4e1e4
fix: native add messages
NathanWalker Jul 6, 2024
4100101
feat: add frameworks to copy/embed phase for any found in use
NathanWalker Jul 8, 2024
1bbd8c1
fix: dev-xcode bump with adding of copy build phase if needed
NathanWalker Jul 8, 2024
0210723
fix: dev-xcode bump with adding of frameworks build phase if needed
NathanWalker Jul 9, 2024
6f409f3
fix: dev-xcode bump with correct frameworks destination handling for …
NathanWalker Jul 9, 2024
3aa8f8b
fix: allow running `ns embed` from outside the project root via `--path`
rigor789 Jul 9, 2024
fcd8807
chore: cleanup [wip]
rigor789 Jul 9, 2024
cfbc741
chore: bump dev-xcode
NathanWalker Jul 9, 2024
1103738
feat: add linker flag for arm64 metadata
NathanWalker Jul 9, 2024
a62fd25
chore: cleanup
rigor789 Jul 10, 2024
d9fe76c
chore: bump nativescript-dev-xcode
rigor789 Jul 11, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,8 @@ injector.requireCommand("build|vision", "./commands/build");
injector.requireCommand("build|visionos", "./commands/build");
injector.requireCommand("deploy", "./commands/deploy");

injector.requireCommand("embed", "./commands/embedding/embed");

injector.require("testExecutionService", "./services/test-execution-service");
injector.requireCommand("dev-test|android", "./commands/test");
injector.requireCommand("dev-test|ios", "./commands/test");
Expand Down
3 changes: 2 additions & 1 deletion lib/commands/add-platform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import { injector } from "../common/yok";

export class AddPlatformCommand
extends ValidatePlatformCommandBase
implements ICommand {
implements ICommand
{
public allowedParameters: ICommandParameter[] = [];

constructor(
Expand Down
127 changes: 127 additions & 0 deletions lib/commands/embedding/embed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { ICommand, ICommandParameter } from "../../common/definitions/commands";
import { injector } from "../../common/yok";
import { PrepareCommand } from "../prepare";
import { PrepareController } from "../../controllers/prepare-controller";
import { IOptions, IPlatformValidationService } from "../../declarations";
import { IProjectConfigService, IProjectData } from "../../definitions/project";
import { IPlatformsDataService } from "../../definitions/platform";
import { PrepareDataService } from "../../services/prepare-data-service";
import { IMigrateController } from "../../definitions/migrate";
import { resolve } from "path";
import { IFileSystem } from "../../common/declarations";
import { color } from "../../color";

export class EmbedCommand extends PrepareCommand implements ICommand {
constructor(
public $options: IOptions,
public $prepareController: PrepareController,
public $platformValidationService: IPlatformValidationService,
public $projectData: IProjectData,
public $platformCommandParameter: ICommandParameter,
public $platformsDataService: IPlatformsDataService,
public $prepareDataService: PrepareDataService,
public $migrateController: IMigrateController,

private $logger: ILogger,
private $fs: IFileSystem,
private $projectConfigService: IProjectConfigService
) {
super(
$options,
$prepareController,
$platformValidationService,
$projectData,
$platformCommandParameter,
$platformsDataService,
$prepareDataService,
$migrateController
);
}

private resolveHostProjectPath(hostProjectPath: string): string {
if (hostProjectPath.charAt(0) === ".") {
// resolve relative to the project dir
const projectDir = this.$projectData.projectDir;
return resolve(projectDir, hostProjectPath);
}

return resolve(hostProjectPath);
}

public async execute(args: string[]): Promise<void> {
const hostProjectPath = args[1];
const resolvedHostProjectPath =
this.resolveHostProjectPath(hostProjectPath);

if (!this.$fs.exists(resolvedHostProjectPath)) {
this.$logger.error(
`The host project path ${color.yellow(
hostProjectPath
)} (resolved to: ${color.yellow.dim(
resolvedHostProjectPath
)}) does not exist.`
);
return;
}

this.$options["hostProjectPath"] = resolvedHostProjectPath;
if (args.length > 2) {
this.$options["hostProjectModuleName"] = args[2];
}

return super.execute(args);
}

public async canExecute(args: string[]): Promise<boolean> {
const canSuperExecute = await super.canExecute(args);

if (!canSuperExecute) {
return false;
}

// args[0] is the platform
// args[1] is the path to the host project
// args[2] is the host project module name

const platform = args[0].toLowerCase();

// also allow these to be set in the nativescript.config.ts
if (!args[1]) {
const hostProjectPath = this.getEmbedConfigForKey(
"hostProjectPath",
platform
);
if (hostProjectPath) {
args[1] = hostProjectPath;
}
}

if (!args[2]) {
const hostProjectModuleName = this.getEmbedConfigForKey(
"hostProjectModuleName",
platform
);
if (hostProjectModuleName) {
args[2] = hostProjectModuleName;
}
}

console.log(args);

if (args.length < 2) {
return false;
}

return true;
}

private getEmbedConfigForKey(key: string, platform: string) {
// get the embed.<platform>.<key> value, or fallback to embed.<key> value
return this.$projectConfigService.getValue(
`embed.${platform}.${key}`,
this.$projectConfigService.getValue(`embed.${key}`)
);
}
}

injector.registerCommand("embed", EmbedCommand);
11 changes: 6 additions & 5 deletions lib/commands/native-add.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { ICommandParameter, ICommand } from "../common/definitions/commands";
import { IErrors } from "../common/declarations";
import * as path from "path";
import { injector } from "../common/yok";
import { capitalizeFirstLetter } from "../common/utils";
import { EOL } from "os";

export class NativeAddCommand implements ICommand {
Expand Down Expand Up @@ -142,7 +143,9 @@ class ${classSimpleName} {
fs.mkdirSync(packagePath, { recursive: true });
fs.writeFileSync(filePath, fileContent);
this.$logger.info(
`${extension} file '${filePath}' generated successfully.`
`${capitalizeFirstLetter(
extension
)} file '${filePath}' generated successfully.`
);
}

Expand All @@ -161,15 +164,12 @@ class ${classSimpleName} {

if (useKotlin === "false") {
this.$errors.failWithHelp(
"The useKotlin property is set to false. Stopping processing."
"The useKotlin property is set to false. Stopping processing. Kotlin must be enabled in gradle.properties to use."
);
return false;
}

if (useKotlin === "true") {
this.$logger.warn(
'gradle.properties already contains "useKotlin=true".'
);
return true;
}
} else {
Expand Down Expand Up @@ -367,6 +367,7 @@ export class NativeAddSwiftCommand extends NativeAddSingleCommand {

const content = `import Foundation;
import os;

@objc class ${className}: NSObject {
@objc func logMessage() {
os_log("Hello from ${className} class!")
Expand Down
25 changes: 16 additions & 9 deletions lib/commands/prepare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import { injector } from "../common/yok";

export class PrepareCommand
extends ValidatePlatformCommandBase
implements ICommand {
implements ICommand
{
public allowedParameters = [this.$platformCommandParameter];

public dashedOptions = {
Expand All @@ -21,17 +22,23 @@ export class PrepareCommand
hasSensitiveValue: false,
},
hmr: { type: OptionType.Boolean, default: false, hasSensitiveValue: false },

whatever: {
type: OptionType.Boolean,
default: false,
hasSensitiveValue: false,
},
};

constructor(
$options: IOptions,
private $prepareController: PrepareController,
$platformValidationService: IPlatformValidationService,
$projectData: IProjectData,
private $platformCommandParameter: ICommandParameter,
$platformsDataService: IPlatformsDataService,
private $prepareDataService: PrepareDataService,
private $migrateController: IMigrateController
public $options: IOptions,
public $prepareController: PrepareController,
public $platformValidationService: IPlatformValidationService,
public $projectData: IProjectData,
public $platformCommandParameter: ICommandParameter,
public $platformsDataService: IPlatformsDataService,
public $prepareDataService: PrepareDataService,
public $migrateController: IMigrateController
) {
super(
$options,
Expand Down
8 changes: 8 additions & 0 deletions lib/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,12 @@ export class Utils implements IUtils {
return timeout * 1000;
}
}

export function capitalizeFirstLetter(value: string) {
if (!value) {
return "";
}
return value.charAt(0).toUpperCase() + value.slice(1);
}

injector.register("utils", Utils);
34 changes: 21 additions & 13 deletions lib/controllers/platform-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,12 +62,13 @@ export class PlatformController implements IPlatformController {

this.$logger.trace("Determined package to install is", packageToInstall);

const installedPlatformVersion = await this.$addPlatformService.addPlatformSafe(
projectData,
platformData,
packageToInstall,
addPlatformData
);
const installedPlatformVersion =
await this.$addPlatformService.addPlatformSafe(
projectData,
platformData,
packageToInstall,
addPlatformData
);

this.$fs.ensureDirectoryExists(
path.join(projectData.platformsDir, platform)
Expand All @@ -80,7 +81,8 @@ export class PlatformController implements IPlatformController {
);
const commentHeader = "# App configuration";
const appPath = projectData.getAppDirectoryRelativePath();
const appResourcesPath = projectData.getAppResourcesRelativeDirectoryPath();
const appResourcesPath =
projectData.getAppResourcesRelativeDirectoryPath();

let gradlePropertiesContents = "";
if (this.$fs.exists(gradlePropertiesPath)) {
Expand Down Expand Up @@ -114,6 +116,12 @@ export class PlatformController implements IPlatformController {
addPlatformData: IAddPlatformData,
projectData?: IProjectData
): Promise<void> {
if (addPlatformData.hostProjectPath) {
this.$logger.trace(
"Not adding platform because --hostProjectPath is provided."
);
return;
}
const [platform] = addPlatformData.platform.toLowerCase().split("@");

projectData ??= this.$projectDataService.getProjectData(
Expand Down Expand Up @@ -161,9 +169,10 @@ export class PlatformController implements IPlatformController {

if (!desiredRuntimePackage.version) {
// if no version is explicitly added, then we use the latest
desiredRuntimePackage.version = await this.$packageInstallationManager.getLatestCompatibleVersion(
desiredRuntimePackage.name
);
desiredRuntimePackage.version =
await this.$packageInstallationManager.getLatestCompatibleVersion(
desiredRuntimePackage.name
);
}
// const currentPlatformData = this.$projectDataService.getNSValue(projectData.projectDir, platformData.frameworkPackageName);
// version = (currentPlatformData && currentPlatformData.version) ||
Expand All @@ -186,9 +195,8 @@ export class PlatformController implements IPlatformController {

const shouldAddNativePlatform =
!nativePrepare || !nativePrepare.skipNativePrepare;
const prepareInfo = this.$projectChangesService.getPrepareInfo(
platformData
);
const prepareInfo =
this.$projectChangesService.getPrepareInfo(platformData);
const requiresNativePlatformAdd =
prepareInfo &&
prepareInfo.nativePlatformStatus ===
Expand Down
11 changes: 8 additions & 3 deletions lib/controllers/prepare-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import {
TrackActionNames,
WEBPACK_COMPILATION_COMPLETE,
} from "../constants";
import { IWatchIgnoreListService } from "../declarations";
import { IOptions, IWatchIgnoreListService } from "../declarations";
import {
INodeModulesDependenciesBuilder,
IPlatformController,
Expand Down Expand Up @@ -59,6 +59,7 @@ export class PrepareController extends EventEmitter {
public $hooksService: IHooksService,
private $fs: IFileSystem,
private $logger: ILogger,
private $options: IOptions,
private $mobileHelper: Mobile.IMobileHelper,
private $nodeModulesDependenciesBuilder: INodeModulesDependenciesBuilder,
private $platformsDataService: IPlatformsDataService,
Expand Down Expand Up @@ -138,6 +139,7 @@ export class PrepareController extends EventEmitter {
prepareData,
projectData
);

await this.trackRuntimeVersion(prepareData.platform, projectData);

this.$logger.info("Preparing project...");
Expand Down Expand Up @@ -426,6 +428,9 @@ export class PrepareController extends EventEmitter {
return patterns;
}

/**
* TODO: move this logic to the webpack side of things - WIP and deprecate here with a webpack version check...
*/
public async writeRuntimePackageJson(
projectData: IProjectData,
platformData: IPlatformData
Expand Down Expand Up @@ -478,9 +483,9 @@ export class PrepareController extends EventEmitter {
} else {
packagePath = path.join(
platformData.projectRoot,
"app",
this.$options.hostProjectModuleName,
"src",
"main",
this.$options.hostProjectPath ? "nativescript" : "main",
"assets",
"app",
"package.json"
Expand Down
4 changes: 4 additions & 0 deletions lib/data/build-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export class IOSBuildData extends BuildData implements IiOSBuildData {
public mobileProvisionData: any;
public buildForAppStore: boolean;
public iCloudContainerEnvironment: string;
public hostProjectPath: string;

constructor(projectDir: string, platform: string, data: any) {
super(projectDir, platform, data);
Expand All @@ -40,6 +41,7 @@ export class IOSBuildData extends BuildData implements IiOSBuildData {
this.mobileProvisionData = data.mobileProvisionData;
this.buildForAppStore = data.buildForAppStore;
this.iCloudContainerEnvironment = data.iCloudContainerEnvironment;
this.hostProjectPath = data.hostProjectPath;
}
}

Expand All @@ -51,6 +53,7 @@ export class AndroidBuildData extends BuildData {
public androidBundle: boolean;
public gradlePath: string;
public gradleArgs: string;
public hostProjectPath: string;

constructor(projectDir: string, platform: string, data: any) {
super(projectDir, platform, data);
Expand All @@ -62,5 +65,6 @@ export class AndroidBuildData extends BuildData {
this.androidBundle = data.androidBundle || data.aab;
this.gradlePath = data.gradlePath;
this.gradleArgs = data.gradleArgs;
this.hostProjectPath = data.hostProjectPath;
}
}
Loading