Smarter resources for Angular apps: CSS pruning, bidirectional layouts, SVG & PNG optimization, Sass compilation, locale permutations, automatic reload.
Disclaimer: This is not an official Google product.
All of the following features are lazy (only triggered when needed) and
most of them are disabled or optimized for speed with pub serve
in debug mode.
(note: may need pub serve --force-poll
on MacOS X)
-
CSS pruning for Angular (see example/angular1, example/angular2):
- Finds which .css rules are not used by Angular templates and removes them.
- Supports
ng-class
andclass
with programmatic interpolated fragments (e.g.class="some-{{fragmented}}-class and-some-normal-class"
,ng-class="{'some-class': isSome}"
). - Disabled by default in debug mode.
-
CSS mirroring / bidirectionalization that works with Angular2's transformer:
-
Uses CSSJanus to produce a single CSS file that supports both RTL & LTR layouts!
-
Given
foo { color: blue; float: left }
, it generates:foo { color: blue; } :host-context([dir="ltr"]) foo { float: left } :host-context([dir="rtl"]) foo { float: right }
So you just need the supporting code in your
main.dart
to support bidirectional layouts (see example/mirroring):document.body.dir = Bidi.isRtlLanguage(Intl.getCurrentLocale()) ? 'rtl' : 'ltr';
-
-
Sass compilation:
- Compiles
*.sass
and*.scss
files withsassc
, the lightning-fast C++ port of Ruby Sass. - Rebuilds
.css
files whenever their.sass
sources are modified.
- Compiles
-
Image inlining:
- Expands inline-image calls in CSS files into data URI links, like Compass does.
- By default in debug mode, links to images instead of inlining them.
-
PNG optimization:
- Calls
pngcrush
to remove all metadata that is useless for rendering. - Disabled by default in debug mode.
- Calls
-
SVG optimization:
- Removes comments, doctypes, unused namespaces.
- Disabled by default in debug mode.
-
Locale-specific permutations generation (Optional, see example/permutations):
- Generates one .js per locale (e.g.
main_en.js
,main_fr.js
...) with the deferred parts needed for that locale, which speeds up load time. - Supports deferred messages and deferred LTR/RTL template caches.
- Optionally optimizes the resulting
.js
files with the Closure Compiler.
- Generates one .js per locale (e.g.
-
Automatic reload support (Optional): zero-turnaround for Dart!
-
Experimental static checker that detects unawaited futures (a common cause of sneaky bugs in async code):
sCiSSors is fine-tuned for fast build in debug
mode (default for pub serve
)
and small code size in release
mode (default for pub build
).
Its behaviour can be fully customized through transformer settings in
pubspec.yaml
.
For instance, to enable PNG optimizations in all modes, and enable SVG
optimizations in debug
only:
transformers:
- scissors:
optimizePng: true
release:
optimizeSvg: false
debug:
optimizeSvg: true
If you checked out scissors
's sources, you can run . ./script/install_dependencies.sh
to get all the required dependencies, and skip the rest of this section :-)
You'll need a local install of CSSJanus for CSS mirroring.
Note that this transformer uses Google's original cssjanus.py, not its .js port (github.com/cssjanus/cssjanus, which might work if packaged as a binary that consumes css from stdin and outputs mirrored css on stdout).
To install CSSJanus, run the following command in a console (ensure you have something like export PATH=~/bin:$PATH
in your ~/.profile
or ~/.bashrc
):
mkdir ~/bin
curl http://cssjanus.googlecode.com/svn/trunk/cssjanus.py > ~/bin/cssjanus.py
chmod +x ~/bin/cssjanus.py
These packages are quite standard, you can get them with brew install
on MacOS X and with sudo apt-get install
on Ubuntu:
sassc
pngcrush
The default transformer will build Sass files in a blink of an
eye and will optimize CSS, PNG and SVG assets in release
mode
(pub build
).
Please only setup sCiSSors's transformer on projects you know respect sCiSSors' conventions and limitations (see below).
Examples: see example/angular1, example/angular2).
pubspec.yaml
:
dev_dependencies:
scissors: ^0.6.0
transformers:
- scissors
Valid settings:
pruneCss
(boolean):false
by defaultimageInlining
: default isinlineInlinedImages
inlineAllUrls
: treatsurl
asinline-image
inlineInlinedImages
: simply honoursinline-image
linkInlinedImages
: replacesinline-image
byurl
disablePass
: leavesinline-image
untouched
optimizePng
(boolean): by default,true
inrelease
onlyoptimizeSvg
(boolean): by default,true
inrelease
onlysasscPath
: default issassc
compiledCssExtension
: default isappend
append
: append the.css
extension to the SASS / SCSS file name:foo.scss
will be compiled tofoo.scss.css
.replace
: replace the SASS / SCSS file extension by.css
:foo.scss
will be compiled tofoo.css
.
pngCrushPath
: default ispngcrush
bidiCss
(boolean): default isfalse
(note that this istrue
by default in thescissors/css_mirroring_transformer
, see below)cssJanusPath
:cssjanus.py
by default, see prerequisites
- Assumes if foo.html exists, foo.css is only used from there (conventions
matter). This means sCiSSors should be disabled or used with caution when
using Angular2 with
ViewEncapsulation.None
(see section below). - Very limited support of CSS rules (naive and hopefully pessimistic matching),
- Bails out of pruning as soon as it doesn't recognize the (map literal)
syntax of an
ng-class
(or if the map has non-string-literal keys), - Does not detect direct / handle DOM manipulations done in .dart companion files yet (html:Element.classes, etc).
- No support for XML namespaces in CSS3 attribute selectors.
- No CSS renaming yet (just pruning for now),
- No Polymer.dart support yet.
Angular(1,2) provide the following strategies:
- Shadow DOM (default in AngularDart 1.x), implemented by
ShadowDomComponentFactory
in AngularDart 1.x andViewEncapsulation.Native
in Angular2 - Shadow DOM emulation with "transclusion" (default in Angular2) implemented by
TranscludingComponentFactory
in AngularDart 1.x andViewEncapsulation.Emulated
in Angular2 - Unscoped / no Shadow DOM, implemented by
ViewEncapsulation.None
in Angular2
The first two strategies (Shadow DOM & its transcluded emulation) provide strict encapsulation of style at the component level: styles defined in a component do not leak to any of its sub-components or parent components. This is the assumption by which sCiSSors lives, so you're safe with it.
The last "unscoped" strategy means there's no file- or component-local way of deciding if a style could be used elsewhere. You should not use sCiSSors on packages / projects with that strategy.
See BidirectionalCss for more details.
Example: see example/mirroring.
pubspec.yaml
:
dev_dependencies:
scissors
transformers:
- scissors/css_mirroring_transformer
Valid settings:
bidiCss
(boolean):true
by default (Note: this is not the same default as in thescissors
transformer)originalCssDirection
(ltr
orrtl
):ltr
by default, defines the direction of input css.cssJanusPath
:cssjanus.py
by default.
- The standalone
scissors/css_mirroring_transformer
transformer only consumes CSS files. If you need Sass support, please use thescissors
transformer withbidiCss: true
. - Does not handle directives like
@keyframes
and@page
.
Example: see example/permutations.
pubspec.yaml
:
dev_dependencies:
scissors
transformers:
- scissors/permutations_transformer
Valid settings:
generatePermutations
:true
by defaultltrImport
andrtlImport
: unset by default. If you're deferred-loading LTR/RTL-specific template caches, these settings should take the alias you're importing them under. See example/permutations for a concrete example.expectedPartCounts
(map of .dart.js artifact to number of expected parts): unset by default. For instance:{ web/main.dart.js: 3 }
.stripSourceMaps
:false
by default. Removes thesourceMappingURL
links from all generated.js
files, to avoid provoking 404s in production when sourcemaps aren't served (relevant only when the$dart2js
transformer has settingsourceMaps: true
).reoptimizePermutations
:false
by default. Whether to optimize permutations with the Closure Compiler.closureCompilerJarPath
:compiler.jar
by defaultjavaPath
:java
by default.
This provides an amazing development turnaround experience, whether you're using the other sCiSSors transformers or not.
With pub serve --force-poll
, as soon as you save an asset (say, foo.scss
)
and it finished building the dependent assets (say, foo.scss.css
), the app
will reload. That's typically before you even have the time to tab-switch to
the browser (+ no need to Ctrl+R).
The transformer ensures the automatic reload logic is removed in release
builds (pub build
), without interfering with source maps.
Example: see example/permutations.
Just edit pubspec.yaml
(note: it's in dev_dependencies
, not dependencies
):
dev_dependencies:
scissors
transformers:
- scissors/reloader/transformer
And edit main.dart
:
import 'package:scissors/reloader/reloader.dart';
main() {
setupReloader();
...
}
Valid settings:
serveTimestamps
(boolean): by default,true
indebug
onlyremoveReloader
(boolean): by default,true
inrelease
only
See UnawaitedFutures for more details.
Keep in mind that this transformer is very experimental, and slow. It aims to complement Dart's new strong-mode analyzer with more static checks, some of which could eventually graduate to the analyzer itself.
Example:
pubspec.yaml
:
dev_dependencies:
scissors
transformers:
- scissors/src/checker/transformer:
unawaitedFutures: error
Valid settings:
unawaitedFutures
(ignore
,warning
orerror
):warning
by default
For things to do, please see issues.
To setup dependencies, please run:
. scripts/install_dependencies.sh
This will download some executables used by Scissors and will export the following environment vars
SASSC_BIN
CSSJANUS_BIN
CLOSURE_COMPILER_JAR
PNGCRUSH_BIN
Please run the following command to test your changes + reformat + analyze sources:
./scripts/presubmit.sh
Please never force-push to master
: use git revert
to revert changes.