Skip to content

Add Lottie integration #298

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 15 commits into from
Sep 28, 2022
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ flutter_gen:
flutter_svg: true
flare_flutter: true
rive: true
lottie: true

colors:
inputs:
Expand Down Expand Up @@ -225,6 +226,8 @@ Widget build(BuildContext context) {
|[flutter_svg](https://pub.dev/packages/flutter_svg)|.svg| `flutter_svg: true` |Assets.images.icons.paint.**svg()**|
|[flare_flutter](https://pub.dev/packages/flare_flutter)|.flr| `flare_flutter: true` |Assets.flare.penguin.**flare()**|
|[rive](https://pub.dev/packages/rive)|.flr| `rive: true` |Assets.rive.vehicles.**rive()**|
|[lottie](https://pub.dev/packages/lottie)|_lottie.json| `lottie: true` |Assets.lottie.hamburgerArrow.**lottie()**|


<br/>

Expand Down Expand Up @@ -725,6 +728,7 @@ flutter_gen:
flutter_svg: false
flare_flutter: false
rive: false
lottie: false

assets:
# Optional
Expand Down
1 change: 1 addition & 0 deletions example/assets/lottie/alarm-clock-lottie-v440.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions example/assets/lottie/geometrical-animation.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions example/assets/lottie/hamburger_arrow.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"assets":[],"layers":[{"ddd":0,"ind":0,"ty":3,"nm":"Rotator","ks":{"o":{"k":0},"r":{"k":[{"i":{"x":[0.56],"y":[1]},"o":{"x":[0.634],"y":[0]},"n":["0p56_1_0p634_0"],"t":19,"s":[0],"e":[190.7]},{"i":{"x":[0.562],"y":[1]},"o":{"x":[0.398],"y":[0]},"n":["0p562_1_0p398_0"],"t":33,"s":[190.7],"e":[176.1]},{"i":{"x":[0.684],"y":[1]},"o":{"x":[0.31],"y":[0]},"n":["0p684_1_0p31_0"],"t":40.5,"s":[176.1],"e":[181.8]},{"i":{"x":[0.684],"y":[1]},"o":{"x":[0.438],"y":[0]},"n":["0p684_1_0p438_0"],"t":55,"s":[181.8],"e":[180]},{"i":{"x":[0.733],"y":[0.733]},"o":{"x":[0.385],"y":[0.385]},"n":["0p733_0p733_0p385_0p385"],"t":71,"s":[180],"e":[180]},{"i":{"x":[0.092],"y":[1]},"o":{"x":[0.406],"y":[0]},"n":["0p092_1_0p406_0"],"t":111,"s":[180],"e":[167.9]},{"i":{"x":[0.341],"y":[1]},"o":{"x":[0.6],"y":[0]},"n":["0p341_1_0p6_0"],"t":116,"s":[167.9],"e":[363]},{"i":{"x":[0.462],"y":[1]},"o":{"x":[0.167],"y":[0]},"n":["0p462_1_0p167_0"],"t":134,"s":[363],"e":[360]},{"t":141}]},"p":{"k":[200.5,149.375,0]},"a":{"k":[60,60,0]},"s":{"k":[100,100,100]}},"ao":0,"ip":0,"op":180,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":1,"ty":4,"nm":"A1","parent":0,"ks":{"o":{"k":100},"r":{"k":[{"i":{"x":[0.56],"y":[1]},"o":{"x":[0.634],"y":[0]},"n":["0p56_1_0p634_0"],"t":19,"s":[0],"e":[-45]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":33,"s":[-45],"e":[-45]},{"i":{"x":[0.341],"y":[1]},"o":{"x":[0.6],"y":[0]},"n":["0p341_1_0p6_0"],"t":116,"s":[-45],"e":[0]},{"t":134}]},"p":{"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.634,"y":0},"n":"0p56_1_0p634_0","t":19,"s":[94.5,82.875,0],"e":[96.2,57.055,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0,"y":0},"o":{"x":0.167,"y":0.167},"n":"0_0_0p167_0p167","t":33,"s":[96.2,57.055,0],"e":[96.2,57.055,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.341,"y":1},"o":{"x":0.6,"y":0},"n":"0p341_1_0p6_0","t":116,"s":[96.2,57.055,0],"e":[94.5,82.875,0],"to":[0,0,0],"ti":[0,0,0]},{"t":134}]},"a":{"k":[35,22.25,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-34,22.25],[35,22.25]],"c":false}},"nm":"Path 1"},{"ty":"st","fillEnabled":true,"c":{"k":[0.4,0.16,0.7,1]},"o":{"k":100},"w":{"k":10},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1"},{"ty":"tm","s":{"k":[{"i":{"x":[0.56],"y":[1]},"o":{"x":[0.634],"y":[0]},"n":["0p56_1_0p634_0"],"t":19,"s":[0],"e":[26]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":33,"s":[26],"e":[26]},{"i":{"x":[0.341],"y":[1]},"o":{"x":[0.6],"y":[0]},"n":["0p341_1_0p6_0"],"t":116,"s":[26],"e":[0]},{"t":134}],"ix":1},"e":{"k":[{"i":{"x":[0.56],"y":[0.56]},"o":{"x":[0.634],"y":[0.634]},"n":["0p56_0p56_0p634_0p634"],"t":19,"s":[100],"e":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":33,"s":[100],"e":[100]},{"i":{"x":[0.341],"y":[0.341]},"o":{"x":[0.6],"y":[0.6]},"n":["0p341_0p341_0p6_0p6"],"t":116,"s":[100],"e":[100]},{"t":134}],"ix":2},"o":{"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1"}],"ip":0,"op":180,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":2,"ty":4,"nm":"A2","parent":0,"ks":{"o":{"k":100},"r":{"k":0},"p":{"k":[60,60.625,0]},"a":{"k":[0.5,0,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-34,0],[35,0]],"c":false}},"nm":"Path 1"},{"ty":"st","fillEnabled":true,"c":{"k":[0.4,0.16,0.7,1]},"o":{"k":100},"w":{"k":10},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1"}],"ip":0,"op":180,"st":0,"bm":0,"sr":1},{"ddd":0,"ind":3,"ty":4,"nm":"A3","parent":0,"ks":{"o":{"k":100},"r":{"k":[{"i":{"x":[0.56],"y":[1]},"o":{"x":[0.634],"y":[0]},"n":["0p56_1_0p634_0"],"t":19,"s":[0],"e":[45]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":33,"s":[45],"e":[45]},{"i":{"x":[0.341],"y":[1]},"o":{"x":[0.6],"y":[0]},"n":["0p341_1_0p6_0"],"t":116,"s":[45],"e":[0]},{"t":134}]},"p":{"k":[{"i":{"x":0.56,"y":1},"o":{"x":0.634,"y":0},"n":"0p56_1_0p634_0","t":19,"s":[94.5,37.125,0],"e":[96.2,64.045,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0,"y":0},"o":{"x":0.167,"y":0.167},"n":"0_0_0p167_0p167","t":33,"s":[96.2,64.045,0],"e":[96.2,64.045,0],"to":[0,0,0],"ti":[0,0,0]},{"i":{"x":0.341,"y":1},"o":{"x":0.6,"y":0},"n":"0p341_1_0p6_0","t":116,"s":[96.2,64.045,0],"e":[94.5,37.125,0],"to":[0,0,0],"ti":[0,0,0]},{"t":134}]},"a":{"k":[35,-23.5,0]},"s":{"k":[100,100,100]}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ks":{"k":{"i":[[0,0],[0,0]],"o":[[0,0],[0,0]],"v":[[-34,-23.5],[35,-23.5]],"c":false}},"nm":"Path 1"},{"ty":"st","fillEnabled":true,"c":{"k":[0.4,0.16,0.7,1]},"o":{"k":100},"w":{"k":10},"lc":1,"lj":1,"ml":4,"nm":"Stroke 1"},{"ty":"tr","p":{"k":[0,0],"ix":2},"a":{"k":[0,0],"ix":1},"s":{"k":[100,100],"ix":3},"r":{"k":0,"ix":6},"o":{"k":100,"ix":7},"sk":{"k":0,"ix":4},"sa":{"k":0,"ix":5},"nm":"Transform"}],"nm":"Shape 1"},{"ty":"tm","s":{"k":[{"i":{"x":[0.56],"y":[1]},"o":{"x":[0.634],"y":[0]},"n":["0p56_1_0p634_0"],"t":19,"s":[0],"e":[26]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":33,"s":[26],"e":[26]},{"i":{"x":[0.341],"y":[1]},"o":{"x":[0.6],"y":[0]},"n":["0p341_1_0p6_0"],"t":116,"s":[26],"e":[0]},{"t":134}],"ix":1},"e":{"k":[{"i":{"x":[0.56],"y":[0.56]},"o":{"x":[0.634],"y":[0.634]},"n":["0p56_0p56_0p634_0p634"],"t":19,"s":[100],"e":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"n":["0p833_0p833_0p167_0p167"],"t":33,"s":[100],"e":[100]},{"i":{"x":[0.341],"y":[0.341]},"o":{"x":[0.6],"y":[0.6]},"n":["0p341_0p341_0p6_0p6"],"t":116,"s":[100],"e":[100]},{"t":134}],"ix":2},"o":{"k":0,"ix":3},"m":1,"ix":2,"nm":"Trim Paths 1"}],"ip":0,"op":180,"st":0,"bm":0,"sr":1}],"v":"4.4.26","ddd":0,"ip":0,"op":180,"fr":30,"w":400,"h":300}
1 change: 1 addition & 0 deletions example/assets/lottie/wrong/rocket-lottie-v439.json

Large diffs are not rendered by default.

90 changes: 90 additions & 0 deletions example/lib/gen/assets.gen.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'package:flutter/services.dart';
import 'package:flare_flutter/flare_actor.dart';
import 'package:flare_flutter/flare_controller.dart';
import 'package:rive/rive.dart';
import 'package:lottie/lottie.dart';

class $AssetsFlareGen {
const $AssetsFlareGen();
Expand Down Expand Up @@ -62,6 +63,28 @@ class $AssetsJsonGen {
List<String> get values => [fruits];
}

class $AssetsLottieGen {
const $AssetsLottieGen();

/// File path: assets/lottie/alarm-clock-lottie-v440.json
LottieGenImage get alarmClockLottieV440 =>
const LottieGenImage('assets/lottie/alarm-clock-lottie-v440.json');

/// File path: assets/lottie/geometrical-animation.json
LottieGenImage get geometricalAnimation =>
const LottieGenImage('assets/lottie/geometrical-animation.json');

/// File path: assets/lottie/hamburger_arrow.json
LottieGenImage get hamburgerArrow =>
const LottieGenImage('assets/lottie/hamburger_arrow.json');

$AssetsLottieWrongGen get wrong => const $AssetsLottieWrongGen();

/// List of all assets
List<LottieGenImage> get values =>
[alarmClockLottieV440, geometricalAnimation, hamburgerArrow];
}

class $AssetsMovieGen {
const $AssetsMovieGen();

Expand Down Expand Up @@ -130,12 +153,23 @@ class $AssetsImagesIconsGen {
List<SvgGenImage> get values => [dartTest, fuchsia, kmm, paint];
}

class $AssetsLottieWrongGen {
const $AssetsLottieWrongGen();

/// File path: assets/lottie/wrong/rocket-lottie-v439.json
String get rocketLottieV439 => 'assets/lottie/wrong/rocket-lottie-v439.json';

/// List of all assets
List<String> get values => [rocketLottieV439];
}

class MyAssets {
MyAssets._();

static const $AssetsFlareGen flare = $AssetsFlareGen();
static const $AssetsImagesGen images = $AssetsImagesGen();
static const $AssetsJsonGen json = $AssetsJsonGen();
static const $AssetsLottieGen lottie = $AssetsLottieGen();
static const $AssetsMovieGen movie = $AssetsMovieGen();
static const $AssetsRiveGen rive = $AssetsRiveGen();
static const $AssetsUnknownGen unknown = $AssetsUnknownGen();
Expand Down Expand Up @@ -328,3 +362,59 @@ class RiveGenImage {

String get path => _assetName;
}

class LottieGenImage {
const LottieGenImage(this._assetName);

final String _assetName;

LottieBuilder lottie({
Animation<double>? controller,
bool? animate,
FrameRate? frameRate,
bool? repeat,
bool? reverse,
LottieDelegates? delegates,
LottieOptions? options,
void Function(LottieComposition)? onLoaded,
LottieImageProviderFactory? imageProviderFactory,
Key? key,
AssetBundle? bundle,
Widget Function(BuildContext, Widget, LottieComposition?)? frameBuilder,
ImageErrorWidgetBuilder? errorBuilder,
double? width,
double? height,
BoxFit? fit,
AlignmentGeometry? alignment,
String? package,
bool? addRepaintBoundary,
FilterQuality? filterQuality,
void Function(String)? onWarning,
}) {
return Lottie.asset(
_assetName,
animate: animate,
frameRate: frameRate,
repeat: repeat,
reverse: reverse,
delegates: delegates,
options: options,
onLoaded: onLoaded,
imageProviderFactory: imageProviderFactory,
key: key,
bundle: bundle,
frameBuilder: frameBuilder,
errorBuilder: errorBuilder,
width: width,
height: height,
fit: fit,
alignment: alignment,
package: package,
addRepaintBoundary: addRepaintBoundary,
filterQuality: filterQuality,
onWarning: onWarning,
);
}

String get path => _assetName;
}
21 changes: 21 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,27 @@ void main() async {
fit: BoxFit.contain,
),
),
SizedBox(
width: 200,
height: 200,
child: MyAssets.lottie.hamburgerArrow.lottie(
fit: BoxFit.contain,
),
),
SizedBox(
width: 200,
height: 200,
child: MyAssets.lottie.geometricalAnimation.lottie(
fit: BoxFit.contain,
),
),
SizedBox(
width: 200,
height: 200,
child: MyAssets.lottie.alarmClockLottieV440.lottie(
fit: BoxFit.contain,
),
),
MyAssets.images.chip1.image(),
Container(
height: 400,
Expand Down
5 changes: 4 additions & 1 deletion example/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ dependencies:
flutter_secure_storage: 5.0.2
auto_route: 3.2.4
gap: 2.0.0
lottie: 1.3.0
lottie: 1.4.1
flutter_layout_grid: 2.0.0
video_player: 2.4.0
audio_service: 0.18.4
Expand Down Expand Up @@ -119,6 +119,7 @@ flutter_gen:
flutter_svg: true
flare_flutter: true
rive: true
lottie: true

assets:
enabled: true
Expand Down Expand Up @@ -169,6 +170,8 @@ flutter:
- pictures/chip5.jpg
- assets/flare/
- assets/rive/
- assets/lottie/
- assets/lottie/wrong/
- assets/movie/
- assets/unknown/
fonts:
Expand Down
7 changes: 7 additions & 0 deletions packages/core/example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ void main() {
fit: BoxFit.contain,
),
),
SizedBox(
width: 200,
height: 200,
child: Assets.lottie.hamburgerArrow.lottie(
fit: BoxFit.contain,
),
),
Image(image: Assets.images.chip1),
Assets.images.icons.kmm.svg(key: const Key("kmm_svg")),
Assets.images.icons.fuchsia.svg(),
Expand Down
16 changes: 10 additions & 6 deletions packages/core/lib/generators/assets_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import 'integrations/flare_integration.dart';
import 'integrations/integration.dart';
import 'integrations/rive_integration.dart';
import 'integrations/svg_integration.dart';
import 'integrations/lottie_integration.dart';

class AssetsGenConfig {
AssetsGenConfig._(
Expand All @@ -29,7 +30,7 @@ class AssetsGenConfig {

factory AssetsGenConfig.fromConfig(File pubspecFile, Config config) {
return AssetsGenConfig._(
pubspecFile.parent.path,
pubspecFile.parent.absolute.path,
config.pubspec.packageName,
config.pubspec.flutterGen,
config.pubspec.flutter.assets,
Expand Down Expand Up @@ -66,6 +67,7 @@ String generateAssets(
SvgIntegration(config.packageParameterLiteral),
if (config.flutterGen.integrations.flareFlutter) FlareIntegration(),
if (config.flutterGen.integrations.rive) RiveIntegration(),
if (config.flutterGen.integrations.lottie) LottieIntegration(),
];

// TODO: This code will be removed.
Expand Down Expand Up @@ -213,15 +215,17 @@ List<String> _getAssetRelativePathList(
.toList();
}

AssetType _constructAssetTree(List<String> assetRelativePathList) {
AssetType _constructAssetTree(
List<String> assetRelativePathList, String rootPath) {
// Relative path is the key
final assetTypeMap = <String, AssetType>{
'.': AssetType('.'),
'.': AssetType(rootPath: rootPath, path: '.'),
};
for (final assetPath in assetRelativePathList) {
var path = assetPath;
while (path != '.') {
assetTypeMap.putIfAbsent(path, () => AssetType(path));
assetTypeMap.putIfAbsent(
path, () => AssetType(rootPath: rootPath, path: path));
path = dirname(path);
}
}
Expand Down Expand Up @@ -309,7 +313,7 @@ String _dotDelimiterStyleDefinition(
final assetsStaticStatements = <_Statement>[];

final assetTypeQueue = ListQueue<AssetType>.from(
_constructAssetTree(assetRelativePathList).children);
_constructAssetTree(assetRelativePathList, config.rootPath).children);

while (assetTypeQueue.isNotEmpty) {
final assetType = assetTypeQueue.removeFirst();
Expand Down Expand Up @@ -404,7 +408,7 @@ String _flatStyleDefinition(
)
.distinct()
.sorted()
.map((relativePath) => AssetType(relativePath))
.map((assetPath) => AssetType(rootPath: config.rootPath, path: assetPath))
.mapToIsUniqueWithoutExtension()
.map(
(e) => _createAssetTypeStatement(
Expand Down
118 changes: 118 additions & 0 deletions packages/core/lib/generators/integrations/lottie_integration.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import 'package:pub_semver/pub_semver.dart';

import '../../settings/asset_type.dart';
import 'integration.dart';
import 'dart:convert';
import 'dart:io';
import 'package:path/path.dart' as p;

class LottieIntegration extends Integration {
// These are required keys for this integration.
static const lottieKeys = [
'w', // width
'h', // height
'ip', // The frame at which the Lottie animation starts at
'op', // The frame at which the Lottie animation ends at
'fr', // frame rate
'v', // // Must include version
'layers', // Must include layers
];

@override
List<String> get requiredImports => [
'package:lottie/lottie.dart',
];

@override
String get classOutput => _classDefinition;

final String _classDefinition = '''class LottieGenImage {
const LottieGenImage(this._assetName);

final String _assetName;

LottieBuilder lottie({
Animation<double>? controller,
bool? animate,
FrameRate? frameRate,
bool? repeat,
bool? reverse,
LottieDelegates? delegates,
LottieOptions? options,
void Function(LottieComposition)? onLoaded,
LottieImageProviderFactory? imageProviderFactory,
Key? key,
AssetBundle? bundle,
Widget Function(BuildContext, Widget, LottieComposition?)? frameBuilder,
ImageErrorWidgetBuilder? errorBuilder,
double? width,
double? height,
BoxFit? fit,
AlignmentGeometry? alignment,
String? package,
bool? addRepaintBoundary,
FilterQuality? filterQuality,
void Function(String)? onWarning,
}) {
return Lottie.asset(
_assetName,
animate: animate,
frameRate: frameRate,
repeat: repeat,
reverse: reverse,
delegates: delegates,
options: options,
onLoaded: onLoaded,
imageProviderFactory: imageProviderFactory,
key: key,
bundle: bundle,
frameBuilder: frameBuilder,
errorBuilder: errorBuilder,
width: width,
height: height,
fit: fit,
alignment: alignment,
package: package,
addRepaintBoundary: addRepaintBoundary,
filterQuality: filterQuality,
onWarning: onWarning,
);
}

String get path => _assetName;
}''';

@override
String get className => 'LottieGenImage';

@override
String classInstantiate(String path) => 'LottieGenImage(\'$path\')';

@override
bool isSupport(AssetType type) => isLottieFile(type);

@override
bool get isConstConstructor => true;

bool isLottieFile(AssetType type) {
if (type.mime != 'application/json') {
return false;
}
try {
final absolutePath = p.join(type.rootPath, type.path);
String input = File(absolutePath).readAsStringSync();
final fileKeys = jsonDecode(input) as Map<String, dynamic>;
if (lottieKeys.every((key) => fileKeys.containsKey(key)) &&
fileKeys['v'] != null) {
var version = Version.parse(fileKeys['v']);
// Lottie version 4.4.0 is the first version that supports BodyMovin.
// https://github.com/xvrh/lottie-flutter/blob/0e7499d82ea1370b6acf023af570395bbb59b42f/lib/src/parser/lottie_composition_parser.dart#L60
return version >= Version(4, 4, 0);
}
} on FormatException catch (e) {
// Catches bad/corrupted json and reports it to user.
stderr.writeln(e.message);
}
return false;
}
}
Loading