Fixes #350 - edge cases in `@import` processing.
authorJakub Pawlowicz <contact@jakubpawlowicz.com>
Sat, 30 Aug 2014 22:53:56 +0000 (23:53 +0100)
committerJakub Pawlowicz <contact@jakubpawlowicz.com>
Sun, 31 Aug 2014 23:07:45 +0000 (00:07 +0100)
History.md
lib/imports/inliner.js
test/unit-test.js

index dd9154c..74b2fb5 100644 (file)
@@ -6,6 +6,7 @@
 [2.2.15 / 2014-xx-xx](https://github.com/jakubpawlowicz/clean-css/compare/v2.2.14...v2.2.15)
 ==================
 
+* Fixed issue [#350](https://github.com/GoalSmashers/clean-css/issues/350) - edge cases in `@import` processing.
 * Fixed issue [#346](https://github.com/GoalSmashers/clean-css/issues/346) - overriding !important by !important.
 * Fixed issue [#345](https://github.com/GoalSmashers/clean-css/issues/345) - URL rebasing for document relative ones.
 * Fixed issue [#343](https://github.com/GoalSmashers/clean-css/issues/343) - too aggressive rgba/hsla minification.
index 02f5103..5f4ff46 100644 (file)
@@ -5,6 +5,7 @@ var https = require('https');
 var url = require('url');
 
 var UrlRewriter = require('../images/url-rewriter');
+var Splitter = require('../text/splitter.js');
 
 var merge = function(source1, source2) {
   var target = {};
@@ -47,7 +48,7 @@ module.exports = function Inliner(context, options) {
     options.visited = options.visited || [];
 
     for (; nextEnd < data.length;) {
-      nextStart = data.indexOf('@import', cursor);
+      nextStart = nextImportAt(data, cursor);
       if (nextStart == -1)
         break;
 
@@ -76,6 +77,18 @@ module.exports = function Inliner(context, options) {
     return processNext(options);
   };
 
+  var nextImportAt = function (data, cursor) {
+    var nextLowerCase = data.indexOf('@import', cursor);
+    var nextUpperCase = data.indexOf('@IMPORT', cursor);
+
+    if (nextLowerCase > -1 && nextUpperCase == -1)
+      return nextLowerCase;
+    else if (nextLowerCase == -1 && nextUpperCase > -1)
+      return nextUpperCase;
+    else
+      return Math.min(nextLowerCase, nextUpperCase);
+  };
+
   var processNext = function(options) {
     if (options._shared.left.length > 0)
       return process.apply(null, options._shared.left.shift());
@@ -154,7 +167,7 @@ module.exports = function Inliner(context, options) {
     options.shallow = data.indexOf('@shallow') > 0;
 
     var importDeclaration = data
-      .substring(data.indexOf(' ', nextStart) + 1, nextEnd)
+      .substring(nextImportAt(data, nextStart) + '@import'.length + 1, nextEnd)
       .replace(/@shallow\)$/, ')')
       .trim();
 
@@ -163,7 +176,7 @@ module.exports = function Inliner(context, options) {
     var isQuoted = /^['"]/.exec(importDeclaration.substring(urlStartsAt, urlStartsAt + 2));
     var urlEndsAt = isQuoted ?
       importDeclaration.indexOf(isQuoted[0], urlStartsAt + 1) :
-      importDeclaration.split(' ')[0].length;
+      new Splitter(' ').split(importDeclaration)[0].length - (viaUrl ? 1 : 0);
 
     var importedFile = importDeclaration
       .substring(urlStartsAt, urlEndsAt)
index c1ab439..d9862ae 100644 (file)
@@ -1397,6 +1397,52 @@ title']{display:block}",
       ".one{color:red}"
     ]
   }, { root: process.cwd() }),
+  'malformed but still valid @import': cssContext({
+    'prefixed with whitespace': [
+      "    @import 'test/data/partials/one.css';",
+      ".one{color:red}"
+    ],
+    'no whitespace between @import and filename': [
+      "@import'test/data/partials/one.css';",
+      ".one{color:red}"
+    ],
+    'extra whitespace between @import and filename': [
+      "@import   'test/data/partials/one.css';",
+      ".one{color:red}"
+    ],
+    'line break between @import and filename': [
+      "@import " + lineBreak + "'test/data/partials/one.css';",
+      ".one{color:red}"
+    ],
+    'extra whitespace prefix in file name': [
+      "@import '  test/data/partials/one.css';",
+      ".one{color:red}"
+    ],
+    'extra whitespace suffix in file name': [
+      "@import 'test/data/partials/one.css   ';",
+      ".one{color:red}"
+    ],
+    'extra whitespace after': [
+      "@import 'test/data/partials/one.css'   ;",
+      ".one{color:red}"
+    ],
+    'uppercase @import': [
+      "@IMPORT 'test/data/partials/one.css';",
+      ".one{color:red}"
+    ],
+    'extra whitespace between url and filename': [
+      "@import url(  test/data/partials/one.css);",
+      ".one{color:red}"
+    ],
+    'extra whitespace prefix in file name - url': [
+      "@import url('   test/data/partials/one.css');",
+      ".one{color:red}"
+    ],
+    'extra whitespace suffix in file name - url': [
+      "@import url('test/data/partials/one.css   ');",
+      ".one{color:red}"
+    ]
+  }, { root: process.cwd() }),
   '@import with absolute paths': cssContext({
     'of an unknown file': [
       "@import url(/fake.css);",