@callstack/brownfield-cli 3.0.0-rc.2 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +37 -0
- package/README.md +1 -1
- package/dist/brownfield/commands/packageAndroid.d.ts.map +1 -1
- package/dist/brownfield/commands/packageAndroid.js +2 -0
- package/dist/brownfield/commands/packageIos.d.ts.map +1 -1
- package/dist/brownfield/commands/packageIos.js +17 -1
- package/dist/brownfield/commands/publishAndroid.d.ts.map +1 -1
- package/dist/brownfield/commands/publishAndroid.js +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/navigation/commands/codegen.d.ts +9 -0
- package/dist/navigation/commands/codegen.d.ts.map +1 -0
- package/dist/navigation/commands/codegen.js +24 -0
- package/dist/navigation/commands/index.d.ts +2 -0
- package/dist/navigation/commands/index.d.ts.map +1 -0
- package/dist/navigation/commands/index.js +1 -0
- package/dist/navigation/config.d.ts +6 -0
- package/dist/navigation/config.d.ts.map +1 -0
- package/dist/navigation/config.js +25 -0
- package/dist/navigation/generators/android.d.ts +4 -0
- package/dist/navigation/generators/android.d.ts.map +1 -0
- package/dist/navigation/generators/android.js +91 -0
- package/dist/navigation/generators/ios.d.ts +4 -0
- package/dist/navigation/generators/ios.d.ts.map +1 -0
- package/dist/navigation/generators/ios.js +141 -0
- package/dist/navigation/generators/models.d.ts +13 -0
- package/dist/navigation/generators/models.d.ts.map +1 -0
- package/dist/navigation/generators/models.js +112 -0
- package/dist/navigation/generators/ts.d.ts +6 -0
- package/dist/navigation/generators/ts.d.ts.map +1 -0
- package/dist/navigation/generators/ts.js +96 -0
- package/dist/navigation/helpers/runNavigationCodegenIfApplicable.d.ts +5 -0
- package/dist/navigation/helpers/runNavigationCodegenIfApplicable.d.ts.map +1 -0
- package/dist/navigation/helpers/runNavigationCodegenIfApplicable.js +11 -0
- package/dist/navigation/index.d.ts +7 -0
- package/dist/navigation/index.d.ts.map +1 -0
- package/dist/navigation/index.js +6 -0
- package/dist/navigation/parser.d.ts +3 -0
- package/dist/navigation/parser.d.ts.map +1 -0
- package/dist/navigation/parser.js +33 -0
- package/dist/navigation/runner.d.ts +8 -0
- package/dist/navigation/runner.d.ts.map +1 -0
- package/dist/navigation/runner.js +126 -0
- package/dist/navigation/spec-discovery.d.ts +3 -0
- package/dist/navigation/spec-discovery.d.ts.map +1 -0
- package/dist/navigation/spec-discovery.js +15 -0
- package/dist/navigation/types.d.ts +24 -0
- package/dist/navigation/types.d.ts.map +1 -0
- package/dist/navigation/types.js +1 -0
- package/package.json +7 -2
- package/src/brownfield/commands/packageAndroid.ts +2 -0
- package/src/brownfield/commands/packageIos.ts +34 -1
- package/src/brownfield/commands/publishAndroid.ts +2 -0
- package/src/index.ts +4 -0
- package/src/navigation/commands/codegen.ts +57 -0
- package/src/navigation/commands/index.ts +1 -0
- package/src/navigation/config.ts +35 -0
- package/src/navigation/generators/android.ts +127 -0
- package/src/navigation/generators/ios.ts +169 -0
- package/src/navigation/generators/models.ts +170 -0
- package/src/navigation/generators/ts.ts +123 -0
- package/src/navigation/helpers/runNavigationCodegenIfApplicable.ts +17 -0
- package/src/navigation/index.ts +10 -0
- package/src/navigation/parser.ts +43 -0
- package/src/navigation/runner.ts +256 -0
- package/src/navigation/spec-discovery.ts +25 -0
- package/src/navigation/types.ts +25 -0
- package/dist/brownfield/utils/index.d.ts +0 -4
- package/dist/brownfield/utils/index.d.ts.map +0 -1
- package/dist/brownfield/utils/index.js +0 -3
- package/dist/brownfield/utils/rn-cli.d.ts +0 -17
- package/dist/brownfield/utils/rn-cli.d.ts.map +0 -1
- package/dist/brownfield/utils/rn-cli.js +0 -31
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
export function generateTurboModuleSpec(methods) {
|
|
4
|
+
const methodSignatures = methods
|
|
5
|
+
.map((method) => {
|
|
6
|
+
const params = method.params
|
|
7
|
+
.map((param) => `${param.name}${param.optional ? '?' : ''}: ${param.type}`)
|
|
8
|
+
.join(', ');
|
|
9
|
+
return ` ${method.name}(${params}): ${method.returnType};`;
|
|
10
|
+
})
|
|
11
|
+
.join('\n');
|
|
12
|
+
return `import { TurboModuleRegistry, type TurboModule } from 'react-native';
|
|
13
|
+
|
|
14
|
+
export interface Spec extends TurboModule {
|
|
15
|
+
${methodSignatures}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default TurboModuleRegistry.getEnforcing<Spec>(
|
|
19
|
+
'NativeBrownfieldNavigation'
|
|
20
|
+
);
|
|
21
|
+
`;
|
|
22
|
+
}
|
|
23
|
+
export function generateIndexTs(methods) {
|
|
24
|
+
const functionImplementations = methods
|
|
25
|
+
.map((method) => {
|
|
26
|
+
const params = method.params
|
|
27
|
+
.map((param) => `${param.name}${param.optional ? '?' : ''}: ${param.type}`)
|
|
28
|
+
.join(', ');
|
|
29
|
+
const args = method.params.map((param) => param.name).join(', ');
|
|
30
|
+
const returnType = method.returnType === 'void' ? '' : `: ${method.returnType}`;
|
|
31
|
+
if (method.isAsync) {
|
|
32
|
+
return ` ${method.name}: async (${params})${returnType} => {
|
|
33
|
+
return NativeBrownfieldNavigation.${method.name}(${args});
|
|
34
|
+
}`;
|
|
35
|
+
}
|
|
36
|
+
return ` ${method.name}: (${params})${returnType} => {
|
|
37
|
+
NativeBrownfieldNavigation.${method.name}(${args});
|
|
38
|
+
}`;
|
|
39
|
+
})
|
|
40
|
+
.join(',\n');
|
|
41
|
+
return `import NativeBrownfieldNavigation from './NativeBrownfieldNavigation';
|
|
42
|
+
|
|
43
|
+
const BrownfieldNavigation = {
|
|
44
|
+
${functionImplementations},
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default BrownfieldNavigation;
|
|
48
|
+
`;
|
|
49
|
+
}
|
|
50
|
+
export function transpileWithConsumerBabel(tsCode, projectRoot, packageRoot) {
|
|
51
|
+
const nodeRequire = createRequire(path.join(projectRoot, 'package.json'));
|
|
52
|
+
const moduleCandidates = [projectRoot, packageRoot];
|
|
53
|
+
function resolveOrThrow(moduleName) {
|
|
54
|
+
for (const modulePath of moduleCandidates) {
|
|
55
|
+
try {
|
|
56
|
+
return nodeRequire.resolve(moduleName, { paths: [modulePath] });
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
// Continue with remaining candidates.
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
throw new Error(`Could not resolve "${moduleName}". Install it in your app devDependencies.`);
|
|
63
|
+
}
|
|
64
|
+
const babelCorePath = resolveOrThrow('@babel/core');
|
|
65
|
+
const rnPresetPath = resolveOrThrow('@react-native/babel-preset');
|
|
66
|
+
const babelCore = nodeRequire(babelCorePath);
|
|
67
|
+
const transformed = babelCore.transformSync(tsCode, {
|
|
68
|
+
filename: 'index.ts',
|
|
69
|
+
babelrc: false,
|
|
70
|
+
configFile: false,
|
|
71
|
+
comments: false,
|
|
72
|
+
compact: true,
|
|
73
|
+
minified: true,
|
|
74
|
+
presets: [[rnPresetPath, {}]],
|
|
75
|
+
});
|
|
76
|
+
if (!transformed?.code) {
|
|
77
|
+
throw new Error('Babel transpilation failed for generated index.ts');
|
|
78
|
+
}
|
|
79
|
+
return transformed.code;
|
|
80
|
+
}
|
|
81
|
+
export function generateIndexDts(methods) {
|
|
82
|
+
const methodSignatures = methods
|
|
83
|
+
.map((method) => {
|
|
84
|
+
const params = method.params
|
|
85
|
+
.map((param) => `${param.name}${param.optional ? '?' : ''}: ${param.type}`)
|
|
86
|
+
.join(', ');
|
|
87
|
+
return ` ${method.name}: (${params}) => ${method.returnType};`;
|
|
88
|
+
})
|
|
89
|
+
.join('\n');
|
|
90
|
+
return `declare const BrownfieldNavigation: {
|
|
91
|
+
${methodSignatures}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export default BrownfieldNavigation;
|
|
95
|
+
`;
|
|
96
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runNavigationCodegenIfApplicable.d.ts","sourceRoot":"","sources":["../../../src/navigation/helpers/runNavigationCodegenIfApplicable.ts"],"names":[],"mappings":"AAIA,wBAAsB,gCAAgC,CACpD,WAAW,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,aAAa,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAAC,CASvD"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { isNavigationInstalled } from '../config.js';
|
|
2
|
+
import { isNavigationSpecPresent } from '../spec-discovery.js';
|
|
3
|
+
import { runNavigationCodegen } from '../runner.js';
|
|
4
|
+
export async function runNavigationCodegenIfApplicable(projectRoot, specPath) {
|
|
5
|
+
const hasNavigation = isNavigationInstalled(projectRoot);
|
|
6
|
+
const hasSpec = hasNavigation && isNavigationSpecPresent(specPath, projectRoot);
|
|
7
|
+
if (hasSpec) {
|
|
8
|
+
await runNavigationCodegen({ specPath, projectRoot });
|
|
9
|
+
}
|
|
10
|
+
return { hasNavigation, hasSpec };
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/navigation/index.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,QAAQ,MAAM,qBAAqB,CAAC;AAEhD,eAAO,MAAM,SAAS,QAAiK,CAAC;AAExL,OAAO,EAAE,QAAQ,EAAE,CAAC;AACpB,mBAAmB,YAAY,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,eAAe,QAAQ,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { styleText } from 'node:util';
|
|
2
|
+
import * as Commands from './commands/index.js';
|
|
3
|
+
export const groupName = `${styleText(['bold', 'blueBright'], '@callstack/brownfield-navigation')}${styleText('whiteBright', ' - native codegen utilities for Brownfield Navigation')}`;
|
|
4
|
+
export { Commands };
|
|
5
|
+
export * from './runner.js';
|
|
6
|
+
export default Commands;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../../src/navigation/parser.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAe,eAAe,EAAE,MAAM,YAAY,CAAC;AAE/D,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,eAAe,EAAE,CAqCvE"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import { Project } from 'ts-morph';
|
|
3
|
+
export function parseNavigationSpec(specPath) {
|
|
4
|
+
if (!fs.existsSync(specPath)) {
|
|
5
|
+
throw new Error(`Spec file not found: ${specPath}`);
|
|
6
|
+
}
|
|
7
|
+
const project = new Project({ skipAddingFilesFromTsConfig: true });
|
|
8
|
+
const sourceFile = project.addSourceFileAtPath(specPath);
|
|
9
|
+
const specInterface = sourceFile.getInterface('BrownfieldNavigationSpec') ??
|
|
10
|
+
sourceFile.getInterface('Spec');
|
|
11
|
+
if (!specInterface) {
|
|
12
|
+
throw new Error('Could not find BrownfieldNavigationSpec or Spec interface in spec file');
|
|
13
|
+
}
|
|
14
|
+
return specInterface.getMethods().map((method) => {
|
|
15
|
+
const name = method.getName();
|
|
16
|
+
const params = method.getParameters().map((param) => {
|
|
17
|
+
const typeNode = param.getTypeNode();
|
|
18
|
+
return {
|
|
19
|
+
name: param.getName(),
|
|
20
|
+
type: typeNode?.getText() ?? 'unknown',
|
|
21
|
+
optional: param.isOptional(),
|
|
22
|
+
};
|
|
23
|
+
});
|
|
24
|
+
const returnTypeNode = method.getReturnTypeNode();
|
|
25
|
+
const returnType = returnTypeNode?.getText() ?? 'void';
|
|
26
|
+
return {
|
|
27
|
+
name,
|
|
28
|
+
params,
|
|
29
|
+
returnType,
|
|
30
|
+
isAsync: returnType.startsWith('Promise<'),
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
interface RunNavigationCodegenOptions {
|
|
2
|
+
specPath?: string;
|
|
3
|
+
dryRun?: boolean;
|
|
4
|
+
projectRoot?: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function runNavigationCodegen({ specPath, dryRun, projectRoot, }: RunNavigationCodegenOptions): Promise<void>;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/navigation/runner.ts"],"names":[],"mappings":"AA2BA,UAAU,2BAA2B;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAwKD,wBAAsB,oBAAoB,CAAC,EACzC,QAAQ,EACR,MAAc,EACd,WAA2B,GAC5B,EAAE,2BAA2B,GAAG,OAAO,CAAC,IAAI,CAAC,CAoD7C"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { logger } from '@rock-js/tools';
|
|
4
|
+
import { DEFAULT_ANDROID_JAVA_PACKAGE, getNavigationPackagePath, } from './config.js';
|
|
5
|
+
import { parseNavigationSpec } from './parser.js';
|
|
6
|
+
import { resolveNavigationSpecPath } from './spec-discovery.js';
|
|
7
|
+
import { generateIndexDts, generateIndexTs, generateTurboModuleSpec, transpileWithConsumerBabel, } from './generators/ts.js';
|
|
8
|
+
import { generateObjCImplementation, generateSwiftDelegate, } from './generators/ios.js';
|
|
9
|
+
import { generateKotlinDelegate, generateKotlinModule, } from './generators/android.js';
|
|
10
|
+
import { generateNavigationModels } from './generators/models.js';
|
|
11
|
+
function getOutputPaths(packageRoot, androidJavaPackageName) {
|
|
12
|
+
const androidPackagePathSegments = androidJavaPackageName.split('.');
|
|
13
|
+
return {
|
|
14
|
+
turboModuleSpec: path.join(packageRoot, 'src', 'NativeBrownfieldNavigation.ts'),
|
|
15
|
+
navigationTs: path.join(packageRoot, 'src', 'index.ts'),
|
|
16
|
+
commonjsIndexJs: path.join(packageRoot, 'lib', 'commonjs', 'index.js'),
|
|
17
|
+
moduleIndexJs: path.join(packageRoot, 'lib', 'module', 'index.js'),
|
|
18
|
+
commonjsIndexDts: path.join(packageRoot, 'lib', 'typescript', 'commonjs', 'src', 'index.d.ts'),
|
|
19
|
+
moduleIndexDts: path.join(packageRoot, 'lib', 'typescript', 'module', 'src', 'index.d.ts'),
|
|
20
|
+
swiftDelegate: path.join(packageRoot, 'ios', 'BrownfieldNavigationDelegate.swift'),
|
|
21
|
+
swiftModels: path.join(packageRoot, 'ios', 'BrownfieldNavigationModels.swift'),
|
|
22
|
+
objcImplementation: path.join(packageRoot, 'ios', 'NativeBrownfieldNavigation.mm'),
|
|
23
|
+
kotlinDelegate: path.join(packageRoot, 'android', 'src', 'main', 'java', ...androidPackagePathSegments, 'BrownfieldNavigationDelegate.kt'),
|
|
24
|
+
kotlinModule: path.join(packageRoot, 'android', 'src', 'main', 'java', ...androidPackagePathSegments, 'NativeBrownfieldNavigationModule.kt'),
|
|
25
|
+
kotlinModels: path.join(packageRoot, 'android', 'src', 'main', 'java', ...androidPackagePathSegments, 'BrownfieldNavigationModels.kt'),
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function writeArtifacts(paths, artifacts) {
|
|
29
|
+
fs.writeFileSync(paths.turboModuleSpec, artifacts.turboModuleSpec);
|
|
30
|
+
logger.success(`Generated ${paths.turboModuleSpec}`);
|
|
31
|
+
fs.writeFileSync(paths.navigationTs, artifacts.indexTs);
|
|
32
|
+
logger.success(`Generated ${paths.navigationTs}`);
|
|
33
|
+
fs.writeFileSync(paths.commonjsIndexJs, artifacts.indexJs);
|
|
34
|
+
logger.success(`Generated ${paths.commonjsIndexJs}`);
|
|
35
|
+
fs.writeFileSync(paths.moduleIndexJs, artifacts.indexJs);
|
|
36
|
+
logger.success(`Generated ${paths.moduleIndexJs}`);
|
|
37
|
+
fs.writeFileSync(paths.commonjsIndexDts, artifacts.indexDts);
|
|
38
|
+
logger.success(`Generated ${paths.commonjsIndexDts}`);
|
|
39
|
+
fs.writeFileSync(paths.moduleIndexDts, artifacts.indexDts);
|
|
40
|
+
logger.success(`Generated ${paths.moduleIndexDts}`);
|
|
41
|
+
fs.writeFileSync(paths.swiftDelegate, artifacts.swiftDelegate);
|
|
42
|
+
logger.success(`Generated ${paths.swiftDelegate}`);
|
|
43
|
+
if (artifacts.swiftModels) {
|
|
44
|
+
fs.writeFileSync(paths.swiftModels, artifacts.swiftModels);
|
|
45
|
+
logger.success(`Generated ${paths.swiftModels}`);
|
|
46
|
+
}
|
|
47
|
+
fs.writeFileSync(paths.objcImplementation, artifacts.objcImplementation);
|
|
48
|
+
logger.success(`Generated ${paths.objcImplementation}`);
|
|
49
|
+
fs.writeFileSync(paths.kotlinDelegate, artifacts.kotlinDelegate);
|
|
50
|
+
logger.success(`Generated ${paths.kotlinDelegate}`);
|
|
51
|
+
fs.writeFileSync(paths.kotlinModule, artifacts.kotlinModule);
|
|
52
|
+
logger.success(`Generated ${paths.kotlinModule}`);
|
|
53
|
+
if (artifacts.kotlinModels) {
|
|
54
|
+
fs.writeFileSync(paths.kotlinModels, artifacts.kotlinModels);
|
|
55
|
+
logger.success(`Generated ${paths.kotlinModels}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function printDryRun(androidJavaPackageName, artifacts) {
|
|
59
|
+
logger.info('\n--- Generated: src/NativeBrownfieldNavigation.ts ---\n');
|
|
60
|
+
logger.log(artifacts.turboModuleSpec);
|
|
61
|
+
logger.info('\n--- Generated: src/index.ts ---\n');
|
|
62
|
+
logger.log(artifacts.indexTs);
|
|
63
|
+
logger.info('\n--- Generated (Babel): lib/{commonjs,module}/index.js ---\n');
|
|
64
|
+
logger.log(artifacts.indexJs);
|
|
65
|
+
logger.info('\n--- Generated: lib/typescript/{commonjs,module}/src/index.d.ts ---\n');
|
|
66
|
+
logger.log(artifacts.indexDts);
|
|
67
|
+
logger.info('\n--- Generated: ios/BrownfieldNavigationDelegate.swift ---\n');
|
|
68
|
+
logger.log(artifacts.swiftDelegate);
|
|
69
|
+
if (artifacts.swiftModels) {
|
|
70
|
+
logger.info('\n--- Generated: ios/BrownfieldNavigationModels.swift ---\n');
|
|
71
|
+
logger.log(artifacts.swiftModels);
|
|
72
|
+
}
|
|
73
|
+
logger.info('\n--- Generated: ios/NativeBrownfieldNavigation.mm ---\n');
|
|
74
|
+
logger.log(artifacts.objcImplementation);
|
|
75
|
+
logger.info(`\n--- Generated: android/src/main/java/${androidJavaPackageName.replaceAll('.', '/')}/BrownfieldNavigationDelegate.kt ---\n`);
|
|
76
|
+
logger.log(artifacts.kotlinDelegate);
|
|
77
|
+
logger.info(`\n--- Generated: android/src/main/java/${androidJavaPackageName.replaceAll('.', '/')}/NativeBrownfieldNavigationModule.kt ---\n`);
|
|
78
|
+
logger.log(artifacts.kotlinModule);
|
|
79
|
+
if (artifacts.kotlinModels) {
|
|
80
|
+
logger.info(`\n--- Generated: android/src/main/java/${androidJavaPackageName.replaceAll('.', '/')}/BrownfieldNavigationModels.kt ---\n`);
|
|
81
|
+
logger.log(artifacts.kotlinModels);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
export async function runNavigationCodegen({ specPath, dryRun = false, projectRoot = process.cwd(), }) {
|
|
85
|
+
const resolvedSpecPath = resolveNavigationSpecPath(specPath, projectRoot);
|
|
86
|
+
if (!fs.existsSync(resolvedSpecPath)) {
|
|
87
|
+
throw new Error(`Spec file not found: ${resolvedSpecPath}`);
|
|
88
|
+
}
|
|
89
|
+
logger.info(`Parsing spec file: ${resolvedSpecPath}`);
|
|
90
|
+
const methods = parseNavigationSpec(resolvedSpecPath);
|
|
91
|
+
if (methods.length === 0) {
|
|
92
|
+
throw new Error('No methods found in spec file');
|
|
93
|
+
}
|
|
94
|
+
logger.info(`Found ${methods.length} method(s): ${methods.map((method) => method.name).join(', ')}`);
|
|
95
|
+
const packageRoot = getNavigationPackagePath(projectRoot);
|
|
96
|
+
const androidJavaPackageName = DEFAULT_ANDROID_JAVA_PACKAGE;
|
|
97
|
+
const indexTs = generateIndexTs(methods);
|
|
98
|
+
const models = await generateNavigationModels({
|
|
99
|
+
specPath: resolvedSpecPath,
|
|
100
|
+
methods,
|
|
101
|
+
kotlinPackageName: androidJavaPackageName,
|
|
102
|
+
});
|
|
103
|
+
const artifacts = {
|
|
104
|
+
turboModuleSpec: generateTurboModuleSpec(methods),
|
|
105
|
+
indexTs,
|
|
106
|
+
indexJs: transpileWithConsumerBabel(indexTs, projectRoot, packageRoot),
|
|
107
|
+
indexDts: generateIndexDts(methods),
|
|
108
|
+
swiftDelegate: generateSwiftDelegate(methods),
|
|
109
|
+
objcImplementation: generateObjCImplementation(methods),
|
|
110
|
+
kotlinDelegate: generateKotlinDelegate(methods, androidJavaPackageName),
|
|
111
|
+
kotlinModule: generateKotlinModule(methods, androidJavaPackageName),
|
|
112
|
+
};
|
|
113
|
+
if (models.modelTypeNames.length > 0) {
|
|
114
|
+
logger.info(`Generating quicktype models for types: ${models.modelTypeNames.join(', ')}`);
|
|
115
|
+
artifacts.swiftModels = models.swiftModels;
|
|
116
|
+
artifacts.kotlinModels = models.kotlinModels;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
logger.info('No complex model types found; skipping quicktype model generation');
|
|
120
|
+
}
|
|
121
|
+
if (dryRun) {
|
|
122
|
+
printDryRun(androidJavaPackageName, artifacts);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
writeArtifacts(getOutputPaths(packageRoot, androidJavaPackageName), artifacts);
|
|
126
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"spec-discovery.d.ts","sourceRoot":"","sources":["../../src/navigation/spec-discovery.ts"],"names":[],"mappings":"AAKA,wBAAgB,yBAAyB,CACvC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,WAAW,EAAE,MAAM,GAClB,MAAM,CAQR;AAED,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,WAAW,GAAE,MAAsB,GAClC,OAAO,CAGT"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { DEFAULT_SPEC_FILENAME } from './config.js';
|
|
4
|
+
export function resolveNavigationSpecPath(specPath, projectRoot) {
|
|
5
|
+
if (specPath) {
|
|
6
|
+
return path.isAbsolute(specPath)
|
|
7
|
+
? specPath
|
|
8
|
+
: path.resolve(projectRoot, specPath);
|
|
9
|
+
}
|
|
10
|
+
return path.resolve(projectRoot, DEFAULT_SPEC_FILENAME);
|
|
11
|
+
}
|
|
12
|
+
export function isNavigationSpecPresent(specPath, projectRoot = process.cwd()) {
|
|
13
|
+
const resolvedSpecPath = resolveNavigationSpecPath(specPath, projectRoot);
|
|
14
|
+
return fs.existsSync(resolvedSpecPath);
|
|
15
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export interface MethodParam {
|
|
2
|
+
name: string;
|
|
3
|
+
type: string;
|
|
4
|
+
optional: boolean;
|
|
5
|
+
}
|
|
6
|
+
export interface MethodSignature {
|
|
7
|
+
name: string;
|
|
8
|
+
params: MethodParam[];
|
|
9
|
+
returnType: string;
|
|
10
|
+
isAsync: boolean;
|
|
11
|
+
}
|
|
12
|
+
export interface GeneratedNavigationArtifacts {
|
|
13
|
+
turboModuleSpec: string;
|
|
14
|
+
indexTs: string;
|
|
15
|
+
indexJs: string;
|
|
16
|
+
indexDts: string;
|
|
17
|
+
swiftDelegate: string;
|
|
18
|
+
objcImplementation: string;
|
|
19
|
+
kotlinDelegate: string;
|
|
20
|
+
kotlinModule: string;
|
|
21
|
+
swiftModels?: string;
|
|
22
|
+
kotlinModels?: string;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/navigation/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,4BAA4B;IAC3C,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@callstack/brownfield-cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Artur Morys-Magiera <artus9033@gmail.com>",
|
|
6
6
|
"bin": {
|
|
@@ -32,6 +32,11 @@
|
|
|
32
32
|
"types": "./dist/brownie/index.d.ts",
|
|
33
33
|
"default": "./dist/brownie/index.js"
|
|
34
34
|
},
|
|
35
|
+
"./navigation": {
|
|
36
|
+
"source": "./src/navigation/index.ts",
|
|
37
|
+
"types": "./dist/navigation/index.d.ts",
|
|
38
|
+
"default": "./dist/navigation/index.js"
|
|
39
|
+
},
|
|
35
40
|
"./package.json": "./package.json"
|
|
36
41
|
},
|
|
37
42
|
"scripts": {
|
|
@@ -100,4 +105,4 @@
|
|
|
100
105
|
"engines": {
|
|
101
106
|
"node": ">=20"
|
|
102
107
|
}
|
|
103
|
-
}
|
|
108
|
+
}
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import { runExpoPrebuildIfNeeded } from '../utils/expo.js';
|
|
15
15
|
import { getProjectInfo } from '../utils/project.js';
|
|
16
16
|
import { runBrownieCodegenIfApplicable } from '../../brownie/helpers/runBrownieCodegenIfApplicable.js';
|
|
17
|
+
import { runNavigationCodegenIfApplicable } from '../../navigation/helpers/runNavigationCodegenIfApplicable.js';
|
|
17
18
|
|
|
18
19
|
export const packageAndroidCommand = curryOptions(
|
|
19
20
|
new Command('package:android').description('Build Android AAR'),
|
|
@@ -31,6 +32,7 @@ export const packageAndroidCommand = curryOptions(
|
|
|
31
32
|
});
|
|
32
33
|
|
|
33
34
|
await runBrownieCodegenIfApplicable(projectRoot, 'kotlin');
|
|
35
|
+
await runNavigationCodegenIfApplicable(projectRoot);
|
|
34
36
|
|
|
35
37
|
await packageAarAction({
|
|
36
38
|
projectRoot,
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
ExampleUsage,
|
|
24
24
|
} from '../../shared/index.js';
|
|
25
25
|
import { runBrownieCodegenIfApplicable } from '../../brownie/helpers/runBrownieCodegenIfApplicable.js';
|
|
26
|
+
import { runNavigationCodegenIfApplicable } from '../../navigation/helpers/runNavigationCodegenIfApplicable.js';
|
|
26
27
|
import { stripFrameworkBinary } from '../utils/stripFrameworkBinary.js';
|
|
27
28
|
|
|
28
29
|
export const packageIosCommand = curryOptions(
|
|
@@ -76,6 +77,7 @@ export const packageIosCommand = curryOptions(
|
|
|
76
77
|
projectRoot,
|
|
77
78
|
'swift'
|
|
78
79
|
);
|
|
80
|
+
const { hasNavigation } = await runNavigationCodegenIfApplicable(projectRoot);
|
|
79
81
|
|
|
80
82
|
await packageIosAction(
|
|
81
83
|
options,
|
|
@@ -116,7 +118,7 @@ export const packageIosCommand = curryOptions(
|
|
|
116
118
|
outputPath: brownieOutputPath,
|
|
117
119
|
});
|
|
118
120
|
|
|
119
|
-
// Strip the binary from
|
|
121
|
+
// Strip the binary from Browniexcframework to make it interface-only.
|
|
120
122
|
// This avoids duplicate symbols when consumer apps embed both BrownfieldLib
|
|
121
123
|
// (which contains Brownie symbols) and Brownie.xcframework.
|
|
122
124
|
stripFrameworkBinary(brownieOutputPath);
|
|
@@ -125,6 +127,37 @@ export const packageIosCommand = curryOptions(
|
|
|
125
127
|
`Brownie.xcframework created at ${colorLink(relativeToCwd(brownieOutputPath))}`
|
|
126
128
|
);
|
|
127
129
|
}
|
|
130
|
+
|
|
131
|
+
if (hasNavigation) {
|
|
132
|
+
const productsPath = path.join(options.buildFolder, 'Build', 'Products');
|
|
133
|
+
const brownfieldNavigationOutputPath = path.join(packageDir, 'BrownfieldNavigation.xcframework');
|
|
134
|
+
|
|
135
|
+
await mergeFrameworks({
|
|
136
|
+
sourceDir: userConfig.project.ios.sourceDir,
|
|
137
|
+
frameworkPaths: [
|
|
138
|
+
path.join(
|
|
139
|
+
productsPath,
|
|
140
|
+
`${configuration}-iphoneos`,
|
|
141
|
+
'BrownfieldNavigation',
|
|
142
|
+
'BrownfieldNavigation.framework'
|
|
143
|
+
),
|
|
144
|
+
path.join(
|
|
145
|
+
productsPath,
|
|
146
|
+
`${configuration}-iphonesimulator`,
|
|
147
|
+
'BrownfieldNavigation',
|
|
148
|
+
'BrownfieldNavigation.framework'
|
|
149
|
+
),
|
|
150
|
+
],
|
|
151
|
+
outputPath: brownfieldNavigationOutputPath,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
stripFrameworkBinary(brownfieldNavigationOutputPath);
|
|
156
|
+
|
|
157
|
+
logger.success(
|
|
158
|
+
`BrownfieldNavigation.xcframework created at ${colorLink(relativeToCwd(brownfieldNavigationOutputPath))}`
|
|
159
|
+
);
|
|
160
|
+
}
|
|
128
161
|
})
|
|
129
162
|
);
|
|
130
163
|
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import { getProjectInfo } from '../utils/project.js';
|
|
15
15
|
import { runExpoPrebuildIfNeeded } from '../utils/expo.js';
|
|
16
16
|
import { runBrownieCodegenIfApplicable } from '../../brownie/helpers/runBrownieCodegenIfApplicable.js';
|
|
17
|
+
import { runNavigationCodegenIfApplicable } from '../../navigation/helpers/runNavigationCodegenIfApplicable.js';
|
|
17
18
|
|
|
18
19
|
export const publishAndroidCommand = curryOptions(
|
|
19
20
|
new Command('publish:android').description(
|
|
@@ -29,6 +30,7 @@ export const publishAndroidCommand = curryOptions(
|
|
|
29
30
|
});
|
|
30
31
|
|
|
31
32
|
await runBrownieCodegenIfApplicable(projectRoot, 'kotlin');
|
|
33
|
+
await runNavigationCodegenIfApplicable(projectRoot);
|
|
32
34
|
|
|
33
35
|
await publishLocalAarAction({
|
|
34
36
|
projectRoot,
|
package/src/index.ts
CHANGED
|
@@ -11,6 +11,9 @@ import brownfieldCommands, {
|
|
|
11
11
|
import brownieCommands, {
|
|
12
12
|
groupName as brownieCommandsGroupName,
|
|
13
13
|
} from './brownie/index.js';
|
|
14
|
+
import navigationCommands, {
|
|
15
|
+
groupName as navigationCommandsGroupName,
|
|
16
|
+
} from './navigation/index.js';
|
|
14
17
|
|
|
15
18
|
const program = new Command();
|
|
16
19
|
|
|
@@ -72,6 +75,7 @@ function registrationHelper(
|
|
|
72
75
|
|
|
73
76
|
registrationHelper(brownfieldCommands, brownfieldCommandsGroupName);
|
|
74
77
|
registrationHelper(brownieCommands, brownieCommandsGroupName);
|
|
78
|
+
registrationHelper(navigationCommands, navigationCommandsGroupName);
|
|
75
79
|
|
|
76
80
|
program.commandsGroup('Utility commands').helpCommand('help [command]');
|
|
77
81
|
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { intro, outro } from '@rock-js/tools';
|
|
3
|
+
|
|
4
|
+
import { actionRunner } from '../../shared/index.js';
|
|
5
|
+
import { runNavigationCodegen } from '../runner.js';
|
|
6
|
+
|
|
7
|
+
interface RunNavigationCodegenCommandOptions {
|
|
8
|
+
dryRun?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
interface NavigationCodegenActionOptions {
|
|
12
|
+
specPath?: string;
|
|
13
|
+
dryRun?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export async function runNavigationCodegenCommand({
|
|
17
|
+
specPath,
|
|
18
|
+
dryRun = false,
|
|
19
|
+
}: NavigationCodegenActionOptions): Promise<void> {
|
|
20
|
+
intro('Running Brownfield Navigation codegen');
|
|
21
|
+
await runNavigationCodegen({
|
|
22
|
+
specPath,
|
|
23
|
+
dryRun,
|
|
24
|
+
});
|
|
25
|
+
outro('Done!');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const navigationCodegenCommand = new Command('navigation:codegen')
|
|
29
|
+
.description(
|
|
30
|
+
'Generate Brownfield Navigation native bindings from brownfield.navigation.ts'
|
|
31
|
+
)
|
|
32
|
+
.argument(
|
|
33
|
+
'[specPath]',
|
|
34
|
+
'Path to navigation spec file (defaults to brownfield.navigation.ts)'
|
|
35
|
+
)
|
|
36
|
+
.option('--dry-run', 'Print generated code without writing files')
|
|
37
|
+
.action(
|
|
38
|
+
actionRunner(
|
|
39
|
+
async (
|
|
40
|
+
...args: Array<string | RunNavigationCodegenCommandOptions | undefined>
|
|
41
|
+
) => {
|
|
42
|
+
const specPath = typeof args[0] === 'string' ? args[0] : undefined;
|
|
43
|
+
const options =
|
|
44
|
+
args.find(
|
|
45
|
+
(
|
|
46
|
+
arg
|
|
47
|
+
): arg is RunNavigationCodegenCommandOptions =>
|
|
48
|
+
typeof arg === 'object' && arg !== null && 'dryRun' in arg
|
|
49
|
+
) ?? {};
|
|
50
|
+
|
|
51
|
+
await runNavigationCodegenCommand({
|
|
52
|
+
specPath,
|
|
53
|
+
dryRun: Boolean(options.dryRun),
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
)
|
|
57
|
+
);
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './codegen.js';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
|
|
4
|
+
export const NAVIGATION_PACKAGE_NAME = '@callstack/brownfield-navigation';
|
|
5
|
+
export const DEFAULT_SPEC_FILENAME = 'brownfield.navigation.ts';
|
|
6
|
+
export const DEFAULT_ANDROID_JAVA_PACKAGE =
|
|
7
|
+
'com.callstack.nativebrownfieldnavigation';
|
|
8
|
+
|
|
9
|
+
export function isNavigationInstalled(
|
|
10
|
+
projectRoot: string = process.cwd()
|
|
11
|
+
): boolean {
|
|
12
|
+
const require = createRequire(path.join(projectRoot, 'package.json'));
|
|
13
|
+
try {
|
|
14
|
+
require.resolve(`${NAVIGATION_PACKAGE_NAME}/package.json`);
|
|
15
|
+
return true;
|
|
16
|
+
} catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function getNavigationPackagePath(
|
|
22
|
+
projectRoot: string = process.cwd()
|
|
23
|
+
): string {
|
|
24
|
+
const require = createRequire(path.join(projectRoot, 'package.json'));
|
|
25
|
+
try {
|
|
26
|
+
const packageJsonPath = require.resolve(
|
|
27
|
+
`${NAVIGATION_PACKAGE_NAME}/package.json`
|
|
28
|
+
);
|
|
29
|
+
return path.dirname(packageJsonPath);
|
|
30
|
+
} catch {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`${NAVIGATION_PACKAGE_NAME} is not installed. Run 'npm install ${NAVIGATION_PACKAGE_NAME}' or 'yarn add ${NAVIGATION_PACKAGE_NAME}'`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|