@callstack/brownfield-cli 1.0.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/dist/brownfield/commands/index.d.ts +4 -0
- package/dist/brownfield/commands/index.d.ts.map +1 -0
- package/dist/brownfield/commands/index.js +3 -0
- package/dist/brownfield/commands/packageAndroid.d.ts +5 -0
- package/dist/brownfield/commands/packageAndroid.d.ts.map +1 -0
- package/dist/brownfield/commands/packageAndroid.js +17 -0
- package/dist/brownfield/commands/packageIos.d.ts +5 -0
- package/dist/brownfield/commands/packageIos.d.ts.map +1 -0
- package/dist/brownfield/commands/packageIos.js +57 -0
- package/dist/brownfield/commands/publishAndroid.d.ts +5 -0
- package/dist/brownfield/commands/publishAndroid.d.ts.map +1 -0
- package/dist/brownfield/commands/publishAndroid.js +14 -0
- package/dist/brownfield/index.d.ts +12 -0
- package/dist/brownfield/index.d.ts.map +1 -0
- package/dist/brownfield/index.js +15 -0
- package/dist/brownfield/types/actionRunner.test.d.ts +2 -0
- package/dist/brownfield/types/actionRunner.test.d.ts.map +1 -0
- package/dist/brownfield/types/actionRunner.test.js +1 -0
- package/dist/brownfield/utils/index.d.ts +3 -0
- package/dist/brownfield/utils/index.d.ts.map +1 -0
- package/dist/brownfield/utils/index.js +2 -0
- package/dist/brownfield/utils/paths.d.ts +8 -0
- package/dist/brownfield/utils/paths.d.ts.map +1 -0
- package/dist/brownfield/utils/paths.js +24 -0
- package/dist/brownfield/utils/rn-cli.d.ts +17 -0
- package/dist/brownfield/utils/rn-cli.d.ts.map +1 -0
- package/dist/brownfield/utils/rn-cli.js +31 -0
- package/dist/brownie/commands/codegen.d.ts +11 -0
- package/dist/brownie/commands/codegen.d.ts.map +1 -0
- package/dist/brownie/commands/codegen.js +96 -0
- package/dist/brownie/commands/index.d.ts +2 -0
- package/dist/brownie/commands/index.d.ts.map +1 -0
- package/dist/brownie/commands/index.js +1 -0
- package/dist/brownie/config.d.ts +21 -0
- package/dist/brownie/config.d.ts.map +1 -0
- package/dist/brownie/config.js +47 -0
- package/dist/brownie/generators/kotlin.d.ts +12 -0
- package/dist/brownie/generators/kotlin.d.ts.map +1 -0
- package/dist/brownie/generators/kotlin.js +69 -0
- package/dist/brownie/generators/swift.d.ts +11 -0
- package/dist/brownie/generators/swift.d.ts.map +1 -0
- package/dist/brownie/generators/swift.js +55 -0
- package/dist/brownie/index.d.ts +7 -0
- package/dist/brownie/index.d.ts.map +1 -0
- package/dist/brownie/index.js +6 -0
- package/dist/brownie/store-discovery.d.ts +10 -0
- package/dist/brownie/store-discovery.d.ts.map +1 -0
- package/dist/brownie/store-discovery.js +75 -0
- package/dist/brownie/types.d.ts +2 -0
- package/dist/brownie/types.d.ts.map +1 -0
- package/dist/brownie/types.js +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +54 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +55 -0
- package/dist/main.d.ts +3 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +3 -0
- package/dist/shared/classes/ExampleUsage.d.ts +6 -0
- package/dist/shared/classes/ExampleUsage.d.ts.map +1 -0
- package/dist/shared/classes/ExampleUsage.js +8 -0
- package/dist/shared/classes/index.d.ts +2 -0
- package/dist/shared/classes/index.d.ts.map +1 -0
- package/dist/shared/classes/index.js +1 -0
- package/dist/shared/index.d.ts +3 -0
- package/dist/shared/index.d.ts.map +1 -0
- package/dist/shared/index.js +2 -0
- package/dist/shared/utils/cli.d.ts +5 -0
- package/dist/shared/utils/cli.d.ts.map +1 -0
- package/dist/shared/utils/cli.js +36 -0
- package/dist/shared/utils/index.d.ts +2 -0
- package/dist/shared/utils/index.d.ts.map +1 -0
- package/dist/shared/utils/index.js +1 -0
- package/package.json +98 -0
- package/src/brownfield/commands/packageAndroid.ts +39 -0
- package/src/brownfield/commands/packageIos.ts +114 -0
- package/src/brownfield/commands/publishAndroid.ts +36 -0
- package/src/brownfield/index.ts +24 -0
- package/src/brownfield/utils/index.ts +2 -0
- package/src/brownfield/utils/paths.ts +40 -0
- package/src/brownfield/utils/rn-cli.ts +58 -0
- package/src/brownie/commands/codegen.ts +139 -0
- package/src/brownie/commands/index.ts +1 -0
- package/src/brownie/config.ts +71 -0
- package/src/brownie/generators/kotlin.ts +113 -0
- package/src/brownie/generators/swift.ts +87 -0
- package/src/brownie/index.ts +11 -0
- package/src/brownie/store-discovery.ts +108 -0
- package/src/brownie/types.ts +1 -0
- package/src/index.ts +84 -0
- package/src/main.ts +5 -0
- package/src/shared/classes/ExampleUsage.ts +6 -0
- package/src/shared/classes/index.ts +1 -0
- package/src/shared/index.ts +2 -0
- package/src/shared/utils/cli.ts +47 -0
- package/src/shared/utils/index.ts +1 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { styleText } from 'node:util';
|
|
2
|
+
|
|
3
|
+
import * as Commands from './commands/index.js';
|
|
4
|
+
|
|
5
|
+
export type * from './types.js';
|
|
6
|
+
export * from './store-discovery.js';
|
|
7
|
+
|
|
8
|
+
export const groupName = `${styleText(['bold', 'blueBright'], '@callstack/brownie')}${styleText('whiteBright', ' - Shared state management CLI for React Native Brownfield')}`;
|
|
9
|
+
|
|
10
|
+
export { Commands };
|
|
11
|
+
export default Commands;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { Project } from 'ts-morph';
|
|
4
|
+
|
|
5
|
+
export interface DiscoveredStore {
|
|
6
|
+
name: string;
|
|
7
|
+
schemaPath: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Recursively finds all *.brownie.ts files in a directory.
|
|
12
|
+
*/
|
|
13
|
+
function findBrownieFiles(dir: string, files: string[] = []): string[] {
|
|
14
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
15
|
+
|
|
16
|
+
for (const entry of entries) {
|
|
17
|
+
const fullPath = path.join(dir, entry.name);
|
|
18
|
+
|
|
19
|
+
if (entry.isDirectory()) {
|
|
20
|
+
if (entry.name === 'node_modules' || entry.name.startsWith('.')) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
findBrownieFiles(fullPath, files);
|
|
24
|
+
} else if (entry.name.endsWith('.brownie.ts')) {
|
|
25
|
+
files.push(fullPath);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return files;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Parses a brownie file and extracts store names from BrownieStores interface.
|
|
34
|
+
*/
|
|
35
|
+
function parseStoresFromFile(
|
|
36
|
+
project: Project,
|
|
37
|
+
filePath: string
|
|
38
|
+
): DiscoveredStore[] {
|
|
39
|
+
const sourceFile = project.addSourceFileAtPath(filePath);
|
|
40
|
+
const stores: DiscoveredStore[] = [];
|
|
41
|
+
|
|
42
|
+
const modules = sourceFile.getModules();
|
|
43
|
+
for (const mod of modules) {
|
|
44
|
+
const moduleName = mod.getName();
|
|
45
|
+
if (
|
|
46
|
+
moduleName !== "'@callstack/brownie'" &&
|
|
47
|
+
moduleName !== '"@callstack/brownie"'
|
|
48
|
+
) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const iface = mod.getInterface('BrownieStores');
|
|
53
|
+
if (!iface) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
for (const prop of iface.getProperties()) {
|
|
58
|
+
stores.push({
|
|
59
|
+
name: prop.getName(),
|
|
60
|
+
schemaPath: filePath,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return stores;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Discovers all brownie stores by parsing BrownieStores interface
|
|
70
|
+
* from declare module '@callstack/brownie' blocks.
|
|
71
|
+
*/
|
|
72
|
+
export function discoverStores(
|
|
73
|
+
rootDir: string = process.cwd()
|
|
74
|
+
): DiscoveredStore[] {
|
|
75
|
+
const brownieFiles = findBrownieFiles(rootDir);
|
|
76
|
+
|
|
77
|
+
if (brownieFiles.length === 0) {
|
|
78
|
+
throw new Error(
|
|
79
|
+
'No brownie store files found. Create a file ending with .brownie.ts ' +
|
|
80
|
+
'(e.g., MyStore.brownie.ts)'
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const project = new Project({ skipAddingFilesFromTsConfig: true });
|
|
85
|
+
const allStores: DiscoveredStore[] = [];
|
|
86
|
+
|
|
87
|
+
for (const filePath of brownieFiles) {
|
|
88
|
+
const stores = parseStoresFromFile(project, filePath);
|
|
89
|
+
allStores.push(...stores);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (allStores.length === 0) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
'No stores found in brownie files. Make sure to declare module ' +
|
|
95
|
+
"'@callstack/brownie' with BrownieStores interface."
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const names = allStores.map((s) => s.name);
|
|
100
|
+
const duplicates = names.filter((n, i) => names.indexOf(n) !== i);
|
|
101
|
+
if (duplicates.length > 0) {
|
|
102
|
+
throw new Error(
|
|
103
|
+
`Duplicate store names found: ${[...new Set(duplicates)].join(', ')}`
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return allStores;
|
|
108
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type Platform = 'swift' | 'kotlin';
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { styleText } from 'node:util';
|
|
2
|
+
|
|
3
|
+
import { logger } from '@rock-js/tools';
|
|
4
|
+
|
|
5
|
+
import { Command } from 'commander';
|
|
6
|
+
|
|
7
|
+
import { ExampleUsage } from './shared/index.js';
|
|
8
|
+
import brownfieldCommands, {
|
|
9
|
+
groupName as brownfieldCommandsGroupName,
|
|
10
|
+
} from './brownfield/index.js';
|
|
11
|
+
import brownieCommands, {
|
|
12
|
+
groupName as brownieCommandsGroupName,
|
|
13
|
+
} from './brownie/index.js';
|
|
14
|
+
|
|
15
|
+
const program = new Command();
|
|
16
|
+
|
|
17
|
+
program
|
|
18
|
+
.name(styleText('magenta', 'brownie'))
|
|
19
|
+
.usage(styleText('yellow', '[options] [command]'))
|
|
20
|
+
.description(
|
|
21
|
+
styleText('magentaBright', 'React Native Brownfield CLI - ') +
|
|
22
|
+
styleText(['magenta', 'bold', 'underline'], 'Brownie')
|
|
23
|
+
)
|
|
24
|
+
.version(process.env.npm_package_version ?? '0.0.0');
|
|
25
|
+
|
|
26
|
+
program
|
|
27
|
+
.optionsGroup('Global options:')
|
|
28
|
+
.option('--verbose', 'enable verbose logging')
|
|
29
|
+
.hook('preAction', () => {
|
|
30
|
+
const opts = program.opts();
|
|
31
|
+
if (opts.verbose) {
|
|
32
|
+
logger.setVerbose(opts.verbose ?? false);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
program.configureHelp({
|
|
37
|
+
styleTitle: (str) => styleText('bold', str),
|
|
38
|
+
styleCommandText: (str) => styleText('cyan', str),
|
|
39
|
+
styleArgumentText: (str) => styleText('yellow', str),
|
|
40
|
+
styleSubcommandText: (str) => styleText('blue', str),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
function registrationHelper(
|
|
44
|
+
commandsRegistration: Record<string, unknown | Command | ExampleUsage>,
|
|
45
|
+
groupName: string
|
|
46
|
+
) {
|
|
47
|
+
program.commandsGroup(groupName);
|
|
48
|
+
|
|
49
|
+
const exampleUsageItems: ExampleUsage[] = [];
|
|
50
|
+
Object.values(commandsRegistration).forEach((commandOrExampleUsage) => {
|
|
51
|
+
if (commandOrExampleUsage instanceof Command) {
|
|
52
|
+
// command
|
|
53
|
+
program.addCommand(commandOrExampleUsage);
|
|
54
|
+
} else if (commandOrExampleUsage instanceof ExampleUsage) {
|
|
55
|
+
// piece of example usage for the command group
|
|
56
|
+
exampleUsageItems.push(commandOrExampleUsage);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
if (exampleUsageItems.length) {
|
|
61
|
+
const longestUsageItemCommandLength = exampleUsageItems.reduce(
|
|
62
|
+
(max, item) => Math.max(max, item.command.length),
|
|
63
|
+
0
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
program.addHelpText(
|
|
67
|
+
'after',
|
|
68
|
+
`\nExamples:\n${exampleUsageItems.map((item) => `\t ${styleText('dim', item.command.padEnd(longestUsageItemCommandLength))}\t${item.description}`).join('\n')}\n`
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
registrationHelper(brownfieldCommands, brownfieldCommandsGroupName);
|
|
74
|
+
registrationHelper(brownieCommands, brownieCommandsGroupName);
|
|
75
|
+
|
|
76
|
+
program.commandsGroup('Utility commands').helpCommand('help [command]');
|
|
77
|
+
|
|
78
|
+
export function runCLI(argv: string[]): void {
|
|
79
|
+
program.parse(argv);
|
|
80
|
+
|
|
81
|
+
if (!argv.slice(2).length) {
|
|
82
|
+
program.outputHelp();
|
|
83
|
+
}
|
|
84
|
+
}
|
package/src/main.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ExampleUsage.js';
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { logger, RockError, type RockCLIOptions } from '@rock-js/tools';
|
|
2
|
+
|
|
3
|
+
import type { Command } from 'commander';
|
|
4
|
+
|
|
5
|
+
export function curryOptions(programCommand: Command, options: RockCLIOptions) {
|
|
6
|
+
options.forEach((option) => {
|
|
7
|
+
if (option.parse) {
|
|
8
|
+
programCommand = programCommand.option(
|
|
9
|
+
option.name,
|
|
10
|
+
option.description,
|
|
11
|
+
option.parse,
|
|
12
|
+
option.default
|
|
13
|
+
);
|
|
14
|
+
} else {
|
|
15
|
+
programCommand = programCommand.option(
|
|
16
|
+
option.name,
|
|
17
|
+
option.description,
|
|
18
|
+
option.default
|
|
19
|
+
);
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
return programCommand;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function actionRunner<T, R>(fn: (...args: T[]) => Promise<R>) {
|
|
27
|
+
return async function wrappedCLIAction(...args: T[]) {
|
|
28
|
+
try {
|
|
29
|
+
await fn(...args);
|
|
30
|
+
} catch (error) {
|
|
31
|
+
if (error instanceof RockError) {
|
|
32
|
+
if (logger.isVerbose()) {
|
|
33
|
+
logger.error(error);
|
|
34
|
+
} else {
|
|
35
|
+
logger.error(error.message);
|
|
36
|
+
if (error.cause) {
|
|
37
|
+
logger.error(`Cause: ${error.cause}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
} else {
|
|
41
|
+
logger.error(`Unexpected error while running command:`, error);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './cli.js';
|