Repository: Polymer/vulcanize (js)

Issue:
Have vulcanize inline <link rel="import" type="css"> tags into the template instead of the dom-module.
Currently, vulcanize inlines `<link rel="import" type="css">` tags directly by swapping it with a `<style></style>`.

However, the correct place for `<style></style>` is inside the `<template>`.

Vulcanize should take the css import and inline it at the top of the `<template>`.

Pull request description:
Force a polymer style import to always go in a dom-module template
Fixes #301

Patch:
diff --git a/lib/matchers.js b/lib/matchers.js
index d7bdcdb..f780e76 100644
--- a/lib/matchers.js
+++ b/lib/matchers.js
@@ -79,6 +79,7 @@ module.exports = {
   polymerElement: p.hasTagName('polymer-element'),
   urlAttrs: urlAttrs,
   targetMatcher: targetMatcher,
+  polymerExternalStyle: polymerExternalStyle,
   JS: jsMatcher,
   CSS: styleMatcher,
   CSS_LINK: externalStyle,
diff --git a/lib/vulcan.js b/lib/vulcan.js
index 7840893..44ed8c5 100644
--- a/lib/vulcan.js
+++ b/lib/vulcan.js
@@ -311,6 +311,8 @@ Vulcan.prototype = {
       var src = dom5.getAttribute(tag, 'href');
       var media = dom5.getAttribute(tag, 'media');
       var uri = url.resolve(href, src);
+      var isPolymerExternalStyle = matchers.polymerExternalStyle(tag);
+
       // let the loader handle the requests
       if (this.isExcludedHref(src)) {
         return Promise.resolve(true);
@@ -324,7 +326,26 @@ Vulcan.prototype = {
           }
           var style = dom5.constructors.element('style');
           dom5.setTextContent(style, '\n' + content + '\n');
-          dom5.replace(tag, style);
+
+          if (isPolymerExternalStyle) {
+            // a polymer expternal style <link type="css" rel="import"> must be
+            // in a <dom-module> to be processed
+            var ownerDomModule = dom5.nodeWalkPrior(tag, dom5.predicates.hasTagName('dom-module'));
+            if (ownerDomModule) {
+              var domTemplate = dom5.query(ownerDomModule, dom5.predicates.hasTagName('template'));
+              if (!domTemplate) {
+                // create a <template>, which has a fragment as childNodes[0]
+                domTemplate = dom5.constructors.element('template');
+                domTemplate.childNodes.push(dom5.constructors.fragment());
+                dom5.append(ownerDomModule, domTemplate);
+              }
+              dom5.remove(tag);
+              // put the style at the top of the dom-module's template
+              this.prepend(domTemplate.childNodes[0], style);
+            }
+          } else {
+            dom5.replace(tag, style);
+          }
         }
       }.bind(this));
     }.bind(this));


Test patch:
diff --git a/test/html/inline-styles.html b/test/html/inline-styles.html
index b1b02d7..8e9be9e 100644
--- a/test/html/inline-styles.html
+++ b/test/html/inline-styles.html
@@ -11,8 +11,6 @@
 <link rel="stylesheet" href="imports/regular-style.css">
 <dom-module id="x-foo">
   <link rel="import" type="css" href="imports/url-style.css">
-  <template>
-  </template>
 </dom-module>
 <script>
   Polymer({
diff --git a/test/test.js b/test/test.js
index c4dadc9..f3bb8ec 100644
--- a/test/test.js
+++ b/test/test.js
@@ -857,7 +857,6 @@ suite('Vulcan', function() {
     });
 
     test('External Scripts and Stylesheets are not removed and media queries are retained', function(done) {
-      var matchers = require('../lib/matchers');
       var input = 'test/html/external-stylesheet.html';
       process(input, function(err, doc) {
         if (err) {
@@ -889,6 +888,38 @@ suite('Vulcan', function() {
       };
       process('/html/default.html', callback, options);
     });
+
+    test('Inlined Polymer styles are moved into the <template>', function(done) {
+      var callback = function(err, doc) {
+        if (err) {
+          return done(err);
+        }
+        var domModule = dom5.query(doc, dom5.predicates.hasTagName('dom-module'));
+        assert(domModule);
+        var template = dom5.query(domModule, dom5.predicates.hasTagName('template'));
+        assert(template);
+        var style = dom5.query(template.childNodes[0], matchers.CSS);
+        assert(style);
+        done();
+      };
+      process('test/html/default.html', callback, options);
+    });
+
+    test('Inlined Polymer styles will force a dom-module to have a template', function(done) {
+      var callback = function(err, doc) {
+        if (err) {
+          return done(err);
+        }
+        var domModule = dom5.query(doc, dom5.predicates.hasTagName('dom-module'));
+        assert(domModule);
+        var template = dom5.query(domModule, dom5.predicates.hasTagName('template'));
+        assert(template);
+        var style = dom5.query(template.childNodes[0], matchers.CSS);
+        assert(style);
+        done();
+      };
+      process('test/html/inline-styles.html', callback, options);
+    });
   });
 
   suite('Add import', function(){