Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
16 changes: 11 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,18 @@ This project follows [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.

## [Unreleased]

### 📝 Documentation
---

## [0.0.1-alpha.4] - 2026-04-06

### ✨ New Features

- **Broadcasting Support**: Add `--without-broadcasting` flag to `install` command with `config/broadcasting.dart` stub and Reverb connection config
- **Conditional Env Vars**: Broadcasting env vars (`BROADCAST_CONNECTION`, `REVERB_*`) only generated when broadcasting is enabled

### 🔧 Improvements

- Update homepage URL to Magic website package page (`https://magic.fluttersdk.com/packages/magic-cli`)
- Add "Website" link to README navigation bar
- Update CLI Commands link in welcome view stub to point to package page
- Update documentation issue template placeholder URL
- **Documentation**: Update homepage URL to Magic website package page, add "Website" link to README, update CLI Commands link in welcome view stub

---

Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ magic install --without-database --without-auth
| `--without-events` | Skip events setup |
| `--without-localization` | Skip localization setup |
| `--without-logging` | Skip logging setup |
| `--without-broadcasting` | Skip broadcasting setup |

<details>
<summary><strong>Generated Structure</strong></summary>
Expand All @@ -147,6 +148,7 @@ lib/
├── config/
│ ├── app.dart
│ ├── auth.dart
│ ├── broadcasting.dart
│ ├── cache.dart
│ ├── database.dart
│ ├── logging.dart
Expand Down
28 changes: 28 additions & 0 deletions assets/stubs/install/broadcasting_config.stub
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import 'package:magic/magic.dart';

/// Broadcasting configuration.
///
/// Defines the default broadcasting connection and available connections.
/// See: https://magic.fluttersdk.com/docs/broadcasting
Map<String, dynamic> get broadcastingConfig => {
'broadcasting': {
'default': env('BROADCAST_CONNECTION', 'null'),
'connections': {
'reverb': {
'driver': 'reverb',
'host': env('REVERB_HOST', 'localhost'),
'port': int.tryParse(env('REVERB_PORT', '8080')) ?? 8080,
'scheme': env('REVERB_SCHEME', 'ws'),
'app_key': env('REVERB_APP_KEY', ''),
'auth_endpoint': '/broadcasting/auth',
'reconnect': true,
'max_reconnect_delay': 30000,
'activity_timeout': 120,
'dedup_buffer_size': 100,
},
'null': {
'driver': 'null',
},
},
},
};
65 changes: 39 additions & 26 deletions lib/src/commands/install_command.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import '../stubs/install_stubs.dart';
/// | `--without-events` | Skip events setup |
/// | `--without-localization` | Skip `assets/lang/` directory |
/// | `--without-logging` | Skip `config/logging.dart` |
/// | `--without-broadcasting` | Skip broadcasting setup |
class InstallCommand extends Command {
@override
String get name => 'install';
Expand All @@ -48,11 +49,7 @@ class InstallCommand extends Command {

@override
void configure(ArgParser parser) {
parser.addFlag(
'without-auth',
help: 'Skip auth setup',
negatable: false,
);
parser.addFlag('without-auth', help: 'Skip auth setup', negatable: false);
parser.addFlag(
'without-database',
help: 'Skip database setup',
Expand All @@ -63,11 +60,7 @@ class InstallCommand extends Command {
help: 'Skip network setup',
negatable: false,
);
parser.addFlag(
'without-cache',
help: 'Skip cache setup',
negatable: false,
);
parser.addFlag('without-cache', help: 'Skip cache setup', negatable: false);
parser.addFlag(
'without-events',
help: 'Skip events setup',
Expand All @@ -83,6 +76,11 @@ class InstallCommand extends Command {
help: 'Skip logging setup',
negatable: false,
);
parser.addFlag(
'without-broadcasting',
help: 'Skip broadcasting setup',
negatable: false,
);
}

@override
Expand All @@ -96,6 +94,7 @@ class InstallCommand extends Command {
final withoutEvents = arguments['without-events'] as bool;
final withoutLocalization = arguments['without-localization'] as bool;
final withoutLogging = arguments['without-logging'] as bool;
final withoutBroadcasting = arguments['without-broadcasting'] as bool;

_createDirectories(
root,
Expand All @@ -112,6 +111,7 @@ class InstallCommand extends Command {
withoutCache: withoutCache,
withoutLogging: withoutLogging,
withoutLocalization: withoutLocalization,
withoutBroadcasting: withoutBroadcasting,
);

_createStarterFiles(root);
Expand All @@ -123,10 +123,11 @@ class InstallCommand extends Command {
withoutNetwork: withoutNetwork,
withoutCache: withoutCache,
withoutLogging: withoutLogging,
withoutBroadcasting: withoutBroadcasting,
);

_patchDefaultWidgetTest(root);
_createEnvFiles(root);
_createEnvFiles(root, withoutBroadcasting: withoutBroadcasting);

_registerEnvAsset(root);

Expand Down Expand Up @@ -167,10 +168,7 @@ class InstallCommand extends Command {
];

if (!withoutEvents) {
appDirs.addAll([
'lib/app/listeners',
'lib/app/events',
]);
appDirs.addAll(['lib/app/listeners', 'lib/app/events']);
}

for (final dir in appDirs) {
Expand Down Expand Up @@ -209,6 +207,7 @@ class InstallCommand extends Command {
required bool withoutCache,
required bool withoutLogging,
required bool withoutLocalization,
required bool withoutBroadcasting,
}) {
final providerImports = <String>[];
final providerEntries = <String>[];
Expand Down Expand Up @@ -239,6 +238,9 @@ class InstallCommand extends Command {
if (!withoutAuth) {
providerEntries.add('(app) => VaultServiceProvider(app),');
}
if (!withoutBroadcasting) {
providerEntries.add('(app) => BroadcastServiceProvider(app),');
}

// Auth providers boot AFTER AppServiceProvider (which registers
// userFactory via setUserFactory). AuthServiceProvider.boot()
Expand Down Expand Up @@ -291,6 +293,12 @@ class InstallCommand extends Command {
InstallStubs.loggingConfigContent(),
);
}
if (!withoutBroadcasting) {
_writeIfNotExists(
path.join(root, 'lib/config/broadcasting.dart'),
InstallStubs.broadcastingConfigContent(),
);
}
}

/// Writes the framework starter files that are always created:
Expand Down Expand Up @@ -342,6 +350,7 @@ class InstallCommand extends Command {
required bool withoutNetwork,
required bool withoutCache,
required bool withoutLogging,
required bool withoutBroadcasting,
}) {
final mainPath = path.join(root, 'lib/main.dart');

Expand All @@ -358,10 +367,7 @@ class InstallCommand extends Command {
"import 'config/view.dart';",
];

final configFactories = <String>[
'() => appConfig',
'() => viewConfig',
];
final configFactories = <String>['() => appConfig', '() => viewConfig'];

if (!withoutAuth) {
configImports.add("import 'config/auth.dart';");
Expand All @@ -383,6 +389,10 @@ class InstallCommand extends Command {
configImports.add("import 'config/logging.dart';");
configFactories.add('() => loggingConfig');
}
if (!withoutBroadcasting) {
configImports.add("import 'config/broadcasting.dart';");
configFactories.add('() => broadcastingConfig');
}

final appName = _getAppName(root);

Expand Down Expand Up @@ -420,26 +430,29 @@ class InstallCommand extends Command {
return;
}

FileHelper.writeFile(
widgetTestPath,
InstallStubs.widgetTestContent(),
);
FileHelper.writeFile(widgetTestPath, InstallStubs.widgetTestContent());
}

/// Writes `.env` and `.env.example` to [root] if they do not already exist.
///
/// [root] — absolute path to the Flutter project root.
void _createEnvFiles(String root) {
/// [withoutBroadcasting] — when `true`, omits broadcasting env vars.
void _createEnvFiles(String root, {required bool withoutBroadcasting}) {
final appName = _getAppName(root);

_writeIfNotExists(
path.join(root, '.env'),
InstallStubs.envContent(appName: appName),
InstallStubs.envContent(
appName: appName,
withoutBroadcasting: withoutBroadcasting,
),
);

_writeIfNotExists(
path.join(root, '.env.example'),
InstallStubs.envExampleContent(),
InstallStubs.envExampleContent(
withoutBroadcasting: withoutBroadcasting,
),
);
}

Expand Down
80 changes: 50 additions & 30 deletions lib/src/stubs/install_stubs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,11 @@ class InstallStubs {
final imports = configImports.join('\n');
final factories = configFactories.map((f) => ' $f,').join('\n');

return StubLoader.replace(
StubLoader.load('install/main'),
{
'configImports': imports,
'configFactories': factories,
'appName': appName,
},
);
return StubLoader.replace(StubLoader.load('install/main'), {
'configImports': imports,
'configFactories': factories,
'appName': appName,
});
}

// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -80,13 +77,10 @@ class InstallStubs {
...authProviderEntries.map((e) => ' $e'),
].join('\n');

return StubLoader.replace(
StubLoader.load('install/app_config'),
{
'allImports': allImports,
'allProviders': allProviders,
},
);
return StubLoader.replace(StubLoader.load('install/app_config'), {
'allImports': allImports,
'allProviders': allProviders,
});
}

/// Generates `lib/config/auth.dart` matching the Uptizm production pattern.
Expand Down Expand Up @@ -124,6 +118,11 @@ class InstallStubs {
return StubLoader.load('install/logging_config');
}

/// Generates `lib/config/broadcasting.dart` with Reverb and null connections.
static String broadcastingConfigContent() {
return StubLoader.load('install/broadcasting_config');
}

// ---------------------------------------------------------------------------
// Service Providers
// ---------------------------------------------------------------------------
Expand Down Expand Up @@ -180,12 +179,9 @@ class InstallStubs {
///
/// [appName] — the human-readable application name shown in the hero section.
static String welcomeViewContent({required String appName}) {
return StubLoader.replace(
StubLoader.load('install/welcome_view'),
{
'appName': appName,
},
);
return StubLoader.replace(StubLoader.load('install/welcome_view'), {
'appName': appName,
});
}

// ---------------------------------------------------------------------------
Expand All @@ -195,20 +191,44 @@ class InstallStubs {
/// Generates a `.env` template file with sensible defaults.
///
/// [appName] — written as the default value for `APP_NAME`.
static String envContent({required String appName}) {
return StubLoader.replace(
StubLoader.load('install/env'),
{
'appName': appName,
},
);
/// [withoutBroadcasting] — when `true`, omits the `BROADCAST_CONNECTION`
/// and `REVERB_*` environment variables.
static String envContent({
required String appName,
bool withoutBroadcasting = false,
}) {
var content = StubLoader.replace(StubLoader.load('install/env'), {
'appName': appName,
});

if (!withoutBroadcasting) {
content += '\nBROADCAST_CONNECTION=null\n'
'REVERB_HOST=localhost\n'
'REVERB_PORT=8080\n'
'REVERB_SCHEME=ws\n'
'REVERB_APP_KEY=\n';
}

return content;
}

/// Generates a `.env.example` template file with empty values.
///
/// Safe to commit — contains keys but no secrets.
static String envExampleContent() {
return StubLoader.load('install/env_example');
/// [withoutBroadcasting] — when `true`, omits the `BROADCAST_CONNECTION`
/// and `REVERB_*` environment variable keys.
static String envExampleContent({bool withoutBroadcasting = false}) {
var content = StubLoader.load('install/env_example');

if (!withoutBroadcasting) {
content += '\nBROADCAST_CONNECTION=\n'
'REVERB_HOST=\n'
'REVERB_PORT=\n'
'REVERB_SCHEME=\n'
'REVERB_APP_KEY=\n';
}

return content;
}

/// Generates a Magic-compatible smoke test for `test/widget_test.dart`.
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: magic_cli
description: "Command-line tools for Magic Framework. Scaffolding, code generation, and project management."
version: 0.0.1-alpha.3
version: 0.0.1-alpha.4
homepage: https://magic.fluttersdk.com/packages/magic-cli
repository: https://github.com/fluttersdk/magic_cli
issue_tracker: https://github.com/fluttersdk/magic_cli/issues
Expand Down
Loading