How to auto run transformer on asset change - dart

Barback package description:
An asset build system. Given a set of input files and a set of
transformations (think compilers, preprocessors and the like), will
automatically apply the appropriate transforms and generate output
files. When inputs are modified, automatically runs the transforms
that are affected. Runs transforms asynchronously and in parallel when
possible to maximize responsiveness.
Docs on assets and transformers say:
For pub serve, the transformers run when the dev server starts up and
whenever a source asset changes. The pub build command runs the
transformers once and then exits.
So I took this example:
// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
import 'package:barback/barback.dart';
import 'package:markdown/markdown.dart';
import 'dart:async';
class ConvertMarkdown extends Transformer {
// A constructor named "asPlugin" is required. It can be empty, but
// it must be present. It is how pub determines that you want this
// class to be publicly available as a loadable transformer plugin.
ConvertMarkdown.asPlugin();
// Any markdown file with one of the following extensions is
// converted to HTML.
String get allowedExtensions => ".md .markdown .mdown";
Future apply(Transform transform) {
return transform.primaryInput.readAsString().then((content) {
// The extension of the output is changed to ".html".
var id = transform.primaryInput.id.changeExtension(".html");
String newContent = "<html><body>"
+ markdownToHtml(content)
+ "</body></html>";
transform.addOutput(new Asset.fromString(id, newContent));
});
}
}
It runs as expected with pub build, but does nothing with pub serve except printing:
Build completed successfully
every time I change any file (not only appropriate asset) in the directory.
After reading this I think that Dart has some problems with watching files(not only directories) on Windows platform.

It is true that pub serve runs transformers after each file modification. But compared to pub build it is not outputting the results to the build/ folder. pub serve is a development server, so you need to request you assets via HTTP, for example in a browser.
See the pub serve documentation for more details

Related

How to get the file path to an asset included in a Dart package?

I am writing a Dart package (not Flutter). I have included a few bitmap images as public assets, e.g., lib/assets/empty.png. When this package is running as a command-line app for an end-user, how can I get the file path to these assets on the user's system?
Use-case: My Dart package calls out to FFMPEG, and I need to tell FFMPEG where to find these asset files on the system that's using my package. For example, the call to FFMPEG might look like:
ffmpeg -i "path/to/lib/assets/empty.png" ...
Accessing a Dart package's assets can happen in two modalities:
Running a Dart CLI app with the dart tool and accessing a dependency's assets, or
Running an executable CLI app
The difference between these two situations is that when you're running a CLI app using the dart tool, all of your dependencies are available as structured packages in a local cache on your system. However, when you're running an executable, all relevant code is compiled into a single binary, which means you no longer have access at runtime to your dependencies' packages, you only have access to your dependencies' tree-shaken, compiled code.
Accessing assets when running with dart
The following code will resolve a package asset URI to a file system path.
final packageUri = Uri.parse('package:your_package/your/asset/path/some_file.whatever');
final future = Isolate.resolvePackageUri(packageUri);
// waitFor is strongly discouraged in general, but it is accepted as the
// only reasonable way to load package assets outside of Flutter.
// ignore: deprecated_member_use
final absoluteUri = waitFor(future, timeout: const Duration(seconds: 5));
final file = File.fromUri(absoluteUri);
if (file.existsSync()) {
return file.path;
}
This resolution code was adapted from Tim Sneath's winmd package: https://github.com/timsneath/winmd/blob/main/lib/src/metadatastore.dart#L84-L106
Accessing assets when running an executable
When compiling a client app to an executable, that client app simply cannot access any asset files that were stored with the dependent package. However, there is a work around that may work for some people (it did for me). You can store Base64 encoded versions of your assets in your Dart code, within your package.
First, encode each of your assets into a Base64 string and store those strings somewhere in your Dart code.
const myAsset = "iVBORw0KGgoAAA....kJggg==";
Then, at runtime, decode the string back to bytes, and then write those bytes to a new file on the local file system. Here's the method I used in my case:
/// Writes this asset to a new file on the host's file system.
///
/// The file is written to [destinationDirectory], or the current
/// working directory, if no destination is provided.
String inflateToLocalFile([Directory? destinationDirectory]) {
final directory = destinationDirectory ?? Directory.current;
final file = File(directory.path + Platform.pathSeparator + fileName);
file.createSync(recursive: true);
final decodedBytes = base64Decode(base64encoded);
file.writeAsBytesSync(decodedBytes);
return file.path;
}
This approach was suggested by #passsy
Have a look at the dcli package.
It has a 'pack' command designed to solve exactly this problem.
It encodes assets into dart files that can be unpacked at runtime.

dart pub build: exclude a file or directory

I am trying to exclude a list of files or directories when building a web application with dart's pub build.
Using this, as suggested by the documentation:
transformers:
- simple_transformer:
$exclude: "**/CVS"
does not work:
Error on line 10, column 3 of pubspec.yaml: "simple_transformer" is not a dependency.
- simple_transformer:
Is there a way to do it (using SDK 1.10.0) ?
Sadly there is currently no support to mark files as ignored by pub build as Günter already mentioned. The .gitignore feature was removed as it was undocumented and caused more trouble than it solved.
But you can execlude files from the build output. This means that the files are still processed (and still take time to process =/ ) but aren't present in the output directiory. This is useful for generating a deployable copy of your application in one go.
In our application we use a simple ConsumeTransformer to mark assets as consumed so that they are not written to the output folder:
library consume_transformer;
import 'package:barback/barback.dart';
class ConsumeTransformer extends Transformer implements LazyTransformer {
final List<RegExp> patterns = <RegExp>[];
ConsumeTransformer.asPlugin(BarbackSettings settings) {
if (settings.configuration['patterns'] != null) {
for (var pattern in settings.configuration['patterns']) {
patterns.add(new RegExp(pattern));
}
}
}
bool isPrimary(AssetId inputId) =>
patterns.any((p) => p.hasMatch(inputId.path));
void declareOutputs(DeclaringTransform transform) {}
void apply(Transform transform) => transform.consumePrimary();
}
The consumer requires a list of regex patterns as an argument an consumes the matched files. You need to add the transformer to you pubspec.yaml file as the last transformer:
transformers:
- ... # Your other transformers
- packagename/consume_transformer:
patterns: ["\\.psd$"]
The example configuration ignores all files that have the psd extension, but you can add pattern as you need them.
I created a pub package that contains the transformer, take a look here.
simple_transformer is the name of the transformer you want to inform to exclude the files. If you want to apply this to dart2js you need to use the name $dart2js instead of simple_transformer.
For more details about configuring $dart2js see https://www.dartlang.org/tools/pub/dart2js-transformer.html

Using tranformer output files from pub run

I'm trying to figure out how I can use transformers for my Dart Server application. So far I've read the articles at https://www.dartlang.org/tools/pub/assets-and-transformers.html and https://www.dartlang.org/tools/pub/transformers/ and have used that information to write the following transformer.
import 'dart:async';
import 'package:barback/barback.dart';
class TestTransformer extends Transformer {
final BarbackSettings _settings;
TestTransformer.asPlugin(this._settings);
AssetId assetId;
Future<bool> isPrimary(AssetId id) {
assetId = id;
return new Future.value(id.toString().endsWith('.txt'));
}
apply(Transform transform) {
transform.primaryInput.readAsString().then((text) {
String output = text.toUpperCase;
print(output);
transform.addOutput(new Asset.fromString(assetId.addExtension('.upper'), output));
});
}
}
When I use pub run I see the transformer run, and the print line outputs as expected. What I'm missing is the *.txt.upper output file. I can't find such a file anywhere on my system. Where does this output end up, and how can I use it in my code? By use I mean being able to read the file with File.open() and, if the output is a Dart file, import it into another Dart file.
As far as I know are transformers for console/server applications not supported. When I run pub upgrade I also see after Precompiling dependencies that some transformers are loaded but this doesn't seem to process your source files.

Is it possible to transform compiled javascript, and to configure the order that transformers are run?

I have a use-case for a pub transformer that isn't very typical, so I'm wondering if it's possible. I'd like to individually gzip every css, html, and javascript file produced during pub build. I have two questions about this:
Is it possible to transform compiled javascript?
Is it possible to configure the order that the transformer is run? Obviously, the gzip transformer would need to be the last transformer run.
In case you're wondering why I want to do this, I'm serving my app via S3 which doesn't support on-the-fly gzipping. I figure pub transformers would be the most appropriate place to do this.
You can specify order by the line position of the transformer:
transformers:
- $dart2js
- YourGzipTransformer
The above specifies that you want the dart2js transformer to run before YourGzipTransformer, thereby giving YourGzipTransformer access to the Javascript files generated by dart2js. If you swapped the order, or did not specify the order of the dart2js transformer, YourGzipTransformer would run before it and not have access to the compiled Javascript.
If you didn't care what order some transformers ran in, you could specify that too:
transformers:
- [$dart2js, SomeOtherTransformer]
- YourGzipTransformer
You can read more about this in the Assets and Transformers article.
Here's a basic transformer that will generate a gzipped version of every CSS, HTML, and Javascript file:
import 'dart:io';
import 'package:barback/barback.dart';
class GzipTransformer extends Transformer {
final BarbackSettings _settings;
GzipTransformer.asPlugin(this._settings);
#override
Future apply(Transform transform) {
return transform.primaryInput.readAsString().then((content) {
var id = transform.primaryInput.id;
var gzipId = id.changeExtension(id.extension +".gzip");
var gzippedContent = GZIP.encode(content.codeUnits);
transform.addOutput(new Asset.fromBytes(gzipId, gzippedContent));
});
}
String get allowedExtensions => ".js .css .html";
}

How to deploy dart polymer with no index.html entry point

I have a dart web application using polymer. I can successfully run it with Dartium using boot.js. However, my index.html file is actually a Django template in another git repo for the project. Its uses template inheritance, among other things, so its not just a normal HTML file.
My goal is to have a Makefile compile the project on request. Currently, pub deploy will compile all the code, and it will run in non-dart browsers. However, my custom polymer elements do not end up being registered. They all show up as blank. Is this kind of setup even possible, that is, to not have an index.html entry point and build custom polymer elements? I could create a dummy buid.html to satisfy the entry-point requirement, but this seems like a sub-optimal solution.
My current buid.dart looks like:
import 'dart:io';
import 'package:polymer/component_build.dart';
import 'package:polymer/deploy.dart' as deploy;
main() {
build(new Options().arguments, [])
.then((_) => deploy.main());
}
and the output:
'package:polymer/component_build.dart': Error: line 68 pos 29: \
ambiguous reference: 'JSON' is defined in library 'dart:convert' \
and also in 'dart:io'
var message = JSON.encode([jsonMessage]);
The only way is to provide some HTML file as entry point. It doesn't matter when you use another HTML file in production if it contains the necessary script tags.

Resources