@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.
Files changed (98) hide show
  1. package/dist/brownfield/commands/index.d.ts +4 -0
  2. package/dist/brownfield/commands/index.d.ts.map +1 -0
  3. package/dist/brownfield/commands/index.js +3 -0
  4. package/dist/brownfield/commands/packageAndroid.d.ts +5 -0
  5. package/dist/brownfield/commands/packageAndroid.d.ts.map +1 -0
  6. package/dist/brownfield/commands/packageAndroid.js +17 -0
  7. package/dist/brownfield/commands/packageIos.d.ts +5 -0
  8. package/dist/brownfield/commands/packageIos.d.ts.map +1 -0
  9. package/dist/brownfield/commands/packageIos.js +57 -0
  10. package/dist/brownfield/commands/publishAndroid.d.ts +5 -0
  11. package/dist/brownfield/commands/publishAndroid.d.ts.map +1 -0
  12. package/dist/brownfield/commands/publishAndroid.js +14 -0
  13. package/dist/brownfield/index.d.ts +12 -0
  14. package/dist/brownfield/index.d.ts.map +1 -0
  15. package/dist/brownfield/index.js +15 -0
  16. package/dist/brownfield/types/actionRunner.test.d.ts +2 -0
  17. package/dist/brownfield/types/actionRunner.test.d.ts.map +1 -0
  18. package/dist/brownfield/types/actionRunner.test.js +1 -0
  19. package/dist/brownfield/utils/index.d.ts +3 -0
  20. package/dist/brownfield/utils/index.d.ts.map +1 -0
  21. package/dist/brownfield/utils/index.js +2 -0
  22. package/dist/brownfield/utils/paths.d.ts +8 -0
  23. package/dist/brownfield/utils/paths.d.ts.map +1 -0
  24. package/dist/brownfield/utils/paths.js +24 -0
  25. package/dist/brownfield/utils/rn-cli.d.ts +17 -0
  26. package/dist/brownfield/utils/rn-cli.d.ts.map +1 -0
  27. package/dist/brownfield/utils/rn-cli.js +31 -0
  28. package/dist/brownie/commands/codegen.d.ts +11 -0
  29. package/dist/brownie/commands/codegen.d.ts.map +1 -0
  30. package/dist/brownie/commands/codegen.js +96 -0
  31. package/dist/brownie/commands/index.d.ts +2 -0
  32. package/dist/brownie/commands/index.d.ts.map +1 -0
  33. package/dist/brownie/commands/index.js +1 -0
  34. package/dist/brownie/config.d.ts +21 -0
  35. package/dist/brownie/config.d.ts.map +1 -0
  36. package/dist/brownie/config.js +47 -0
  37. package/dist/brownie/generators/kotlin.d.ts +12 -0
  38. package/dist/brownie/generators/kotlin.d.ts.map +1 -0
  39. package/dist/brownie/generators/kotlin.js +69 -0
  40. package/dist/brownie/generators/swift.d.ts +11 -0
  41. package/dist/brownie/generators/swift.d.ts.map +1 -0
  42. package/dist/brownie/generators/swift.js +55 -0
  43. package/dist/brownie/index.d.ts +7 -0
  44. package/dist/brownie/index.d.ts.map +1 -0
  45. package/dist/brownie/index.js +6 -0
  46. package/dist/brownie/store-discovery.d.ts +10 -0
  47. package/dist/brownie/store-discovery.d.ts.map +1 -0
  48. package/dist/brownie/store-discovery.js +75 -0
  49. package/dist/brownie/types.d.ts +2 -0
  50. package/dist/brownie/types.d.ts.map +1 -0
  51. package/dist/brownie/types.js +1 -0
  52. package/dist/cli.d.ts +3 -0
  53. package/dist/cli.d.ts.map +1 -0
  54. package/dist/cli.js +54 -0
  55. package/dist/index.d.ts +2 -0
  56. package/dist/index.d.ts.map +1 -0
  57. package/dist/index.js +55 -0
  58. package/dist/main.d.ts +3 -0
  59. package/dist/main.d.ts.map +1 -0
  60. package/dist/main.js +3 -0
  61. package/dist/shared/classes/ExampleUsage.d.ts +6 -0
  62. package/dist/shared/classes/ExampleUsage.d.ts.map +1 -0
  63. package/dist/shared/classes/ExampleUsage.js +8 -0
  64. package/dist/shared/classes/index.d.ts +2 -0
  65. package/dist/shared/classes/index.d.ts.map +1 -0
  66. package/dist/shared/classes/index.js +1 -0
  67. package/dist/shared/index.d.ts +3 -0
  68. package/dist/shared/index.d.ts.map +1 -0
  69. package/dist/shared/index.js +2 -0
  70. package/dist/shared/utils/cli.d.ts +5 -0
  71. package/dist/shared/utils/cli.d.ts.map +1 -0
  72. package/dist/shared/utils/cli.js +36 -0
  73. package/dist/shared/utils/index.d.ts +2 -0
  74. package/dist/shared/utils/index.d.ts.map +1 -0
  75. package/dist/shared/utils/index.js +1 -0
  76. package/package.json +98 -0
  77. package/src/brownfield/commands/packageAndroid.ts +39 -0
  78. package/src/brownfield/commands/packageIos.ts +114 -0
  79. package/src/brownfield/commands/publishAndroid.ts +36 -0
  80. package/src/brownfield/index.ts +24 -0
  81. package/src/brownfield/utils/index.ts +2 -0
  82. package/src/brownfield/utils/paths.ts +40 -0
  83. package/src/brownfield/utils/rn-cli.ts +58 -0
  84. package/src/brownie/commands/codegen.ts +139 -0
  85. package/src/brownie/commands/index.ts +1 -0
  86. package/src/brownie/config.ts +71 -0
  87. package/src/brownie/generators/kotlin.ts +113 -0
  88. package/src/brownie/generators/swift.ts +87 -0
  89. package/src/brownie/index.ts +11 -0
  90. package/src/brownie/store-discovery.ts +108 -0
  91. package/src/brownie/types.ts +1 -0
  92. package/src/index.ts +84 -0
  93. package/src/main.ts +5 -0
  94. package/src/shared/classes/ExampleUsage.ts +6 -0
  95. package/src/shared/classes/index.ts +1 -0
  96. package/src/shared/index.ts +2 -0
  97. package/src/shared/utils/cli.ts +47 -0
  98. 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,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { runCLI } from './index.js';
4
+
5
+ runCLI(process.argv);
@@ -0,0 +1,6 @@
1
+ export class ExampleUsage {
2
+ constructor(
3
+ public readonly command: string,
4
+ public readonly description: string
5
+ ) {}
6
+ }
@@ -0,0 +1 @@
1
+ export * from './ExampleUsage.js';
@@ -0,0 +1,2 @@
1
+ export * from './utils/index.js';
2
+ export * from './classes/index.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';