diff --git a/aio/package.json b/aio/package.json
index e07bd2521c..16c65b81ed 100644
--- a/aio/package.json
+++ b/aio/package.json
@@ -68,8 +68,10 @@
"firebase-tools": "^3.2.1",
"fs-extra": "^2.1.2",
"globby": "^6.1.0",
+ "hast-util-is-element": "^1.0.0",
"html": "^1.0.0",
"http-server": "^0.9.0",
+ "image-size": "^0.5.1",
"jasmine-core": "~2.5.2",
"jasmine-spec-reporter": "~3.2.0",
"jsdom": "^9.12.0",
@@ -94,6 +96,8 @@
"ts-node": "~2.0.0",
"tslint": "~4.5.0",
"typescript": "2.2.0",
+ "unist-util-source": "^1.0.1",
+ "unist-util-visit": "^1.1.1",
"vrsource-tslint-rules": "^4.0.1",
"watchr": "^3.0.1",
"yargs": "^7.0.2"
diff --git a/aio/tools/transforms/angular-base-package/index.js b/aio/tools/transforms/angular-base-package/index.js
index 9015325dc4..58580d02bb 100644
--- a/aio/tools/transforms/angular-base-package/index.js
+++ b/aio/tools/transforms/angular-base-package/index.js
@@ -34,6 +34,9 @@ module.exports = new Package('angular-base', [
.factory('packageInfo', function() { return require(path.resolve(PROJECT_ROOT, 'package.json')); })
.factory(require('./readers/json'))
.factory(require('./services/copyFolder'))
+ .factory(require('./services/getImageDimensions'))
+
+ .factory(require('./post-processors/add-image-dimensions'))
.config(function(checkAnchorLinksProcessor) {
// This is disabled here to prevent false negatives for the `docs-watch` task.
@@ -120,9 +123,11 @@ module.exports = new Package('angular-base', [
})
- .config(function(postProcessHtml) {
+ .config(function(postProcessHtml, addImageDimensions) {
+ addImageDimensions.basePath = path.resolve(AIO_PATH, 'src');
postProcessHtml.plugins = [
- require('./post-processors/autolink-headings')
+ require('./post-processors/autolink-headings'),
+ addImageDimensions
];
})
diff --git a/aio/tools/transforms/angular-base-package/post-processors/add-image-dimensions.js b/aio/tools/transforms/angular-base-package/post-processors/add-image-dimensions.js
new file mode 100644
index 0000000000..1a068d00ee
--- /dev/null
+++ b/aio/tools/transforms/angular-base-package/post-processors/add-image-dimensions.js
@@ -0,0 +1,40 @@
+const visit = require('unist-util-visit');
+const is = require('hast-util-is-element');
+const source = require('unist-util-source');
+
+/**
+ * Add the width and height of the image to the `img` tag if they are
+ * not already provided. This helps prevent jank when the page is
+ * rendered before the image has downloaded.
+ *
+ * If there is no `src` attribute on an image, or it is not possible
+ * to load the image file indicated by the `src` then a warning is emitted.
+ */
+module.exports = function addImageDimensions(getImageDimensions) {
+ return function addImageDimensionsImpl() {
+ return (ast, file) => {
+ visit(ast, node => {
+
+ if (is(node, 'img')) {
+ const props = node.properties;
+ const src = props.src;
+ if (!src) {
+ file.message('Missing src in image tag `' + source(node, file) + '`');
+ } else if (props.width === undefined && props.height === undefined) {
+ try {
+ const dimensions = getImageDimensions(addImageDimensionsImpl.basePath, src);
+ props.width = '' + dimensions.width;
+ props.height = '' + dimensions.height;
+ } catch(e) {
+ if (e.code === 'ENOENT') {
+ file.message('Unable to load src in image tag `' + source(node, file) + '`');
+ } else {
+ file.fail(e.message);
+ }
+ }
+ }
+ }
+ });
+ };
+ };
+};
diff --git a/aio/tools/transforms/angular-base-package/post-processors/add-image-dimensions.spec.js b/aio/tools/transforms/angular-base-package/post-processors/add-image-dimensions.spec.js
new file mode 100644
index 0000000000..965871f6b0
--- /dev/null
+++ b/aio/tools/transforms/angular-base-package/post-processors/add-image-dimensions.spec.js
@@ -0,0 +1,110 @@
+var createTestPackage = require('../../helpers/test-package');
+var Dgeni = require('dgeni');
+
+describe('addImageDimensions post-processor', () => {
+ let processor, getImageDimensionsSpy, addImageDimensions, log;
+
+ beforeEach(() => {
+ const testPackage = createTestPackage('angular-base-package')
+ .factory('getImageDimensions', mockGetImageDimensions);
+ const dgeni = new Dgeni([testPackage]);
+ const injector = dgeni.configureInjector();
+ log = injector.get('log');
+ addImageDimensions = injector.get('addImageDimensions');
+ addImageDimensions.basePath = 'base/path';
+ getImageDimensionsSpy = injector.get('getImageDimensions');
+ processor = injector.get('postProcessHtml');
+ processor.docTypes = ['a'];
+ processor.plugins = [addImageDimensions];
+ });
+
+ it('should add the image dimensions into tags', () => {
+ const docs = [{
+ docType: 'a',
+ renderedContent: `
+
xxx
+yyy
+zzz
+ ` + }]; + processor.$process(docs); + expect(getImageDimensionsSpy).toHaveBeenCalledWith('base/path', 'a/b.jpg'); + expect(getImageDimensionsSpy).toHaveBeenCalledWith('base/path', 'c/d.png'); + expect(docs).toEqual([{ + docType: 'a', + renderedContent: ` +xxx
+yyy
+zzz
+ ` + }]); + }); + + it('should log a warning for images with no src attribute', () => { + const docs = [{ + docType: 'a', + renderedContent: '