Permalink
Browse files

first commit

  • Loading branch information...
0 parents commit 6744b8d46d6243bbfb9b5cc07980f25853f4ce2a @sergeche committed Nov 22, 2012
Showing with 1,945 additions and 0 deletions.
  1. +2 −0 .gitignore
  2. +22 −0 LICENSE-MIT.txt
  3. +42 −0 grunt.js
  4. +125 −0 lib/css.js
  5. +907 −0 lib/parser.js
  6. +47 −0 package.json
  7. +265 −0 tasks/frontend.js
  8. +7 −0 test/css/_utf.css
  9. +253 −0 test/css/assets/_about.css
  10. +62 −0 test/css/assets/_btn.css
  11. +126 −0 test/css/assets/_city_chooser.css
  12. +9 −0 test/css/test-utf.css
  13. +32 −0 test/css/test.css
  14. +23 −0 test/frontend.js
  15. +4 −0 test/js/file1.js
  16. +19 −0 test/js/file2.js
@@ -0,0 +1,2 @@
+/node_modules/
+tmp
@@ -0,0 +1,22 @@
+Copyright (c) 2012 Sergey Chikuyonok
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,42 @@
+"use strict";
+
+module.exports = function(grunt) {
+
+ // Project configuration.
+ grunt.initConfig({
+ test: {
+ files: ['test/*.js']
+ },
+ lint: {
+ files: ['grunt.js', 'tasks/**/*.js', 'test/*.js']
+ },
+ watch: {
+ files: '<config:lint.files>',
+ tasks: 'default'
+ },
+ jshint: {
+ options: {
+ curly: true,
+ eqeqeq: false,
+ immed: true,
+ latedef: true,
+ newcap: true,
+ noarg: true,
+ sub: true,
+ undef: true,
+ boss: true,
+ eqnull: true,
+ node: true,
+ es5: true
+ },
+ globals: {}
+ }
+ });
+
+ // Load local tasks.
+ grunt.loadTasks('tasks');
+
+ // Default task.
+ grunt.registerTask('default', 'lint test');
+
+};
@@ -0,0 +1,125 @@
+var csso = require('csso');
+var parser = require('../lib/parser');
+var _ = require('underscore');
+var fs = require('fs');
+var path = require('path');
+
+function isToken(token, type) {
+ return token instanceof Array && token[1] == type;
+}
+
+function findToken(list, type) {
+ return _.find(list, function(item) {
+ return isToken(item, type);
+ });
+}
+
+/**
+ * Finds all @imported files in CSS file and returns their paths and locations
+ * inside CSS
+ */
+function findImports(css) {
+ var tokens = parser.parse(css, 'stylesheet');
+ var imports = [];
+ tokens.forEach(function(token, i) {
+ if (isToken(token, 'atrules')) {
+ // is it @import rule?
+ var kw = findToken(token, 'atkeyword');
+ if (kw && kw[2][2].toLowerCase() == 'import') {
+ var valueToken;
+ var urlToken = findToken(token, 'uri');
+
+ if (urlToken) {
+ valueToken = findToken(urlToken, 'raw') || findToken(urlToken, 'string');
+ } else {
+ valueToken = findToken(urlToken, 'string');
+ }
+ if (!valueToken) return;
+
+ var ruleStart = token[0].f;
+ var ruleEnd = token[0].l;
+ if (css.charAt(ruleEnd) == ';') {
+ ruleEnd++;
+ }
+
+ imports.push({
+ file: valueToken[2].replace(/^['"]|['"]$/g, ''),
+ start: ruleStart,
+ end: ruleEnd,
+ value: css.substring(ruleStart, ruleEnd)
+ });
+ }
+ }
+ });
+
+ return imports;
+}
+
+/**
+ * Compiles singe CSS file: concats all @imported file into singe one
+ * @param {String} file Absolute path to CSS file
+ * @param {Function} pathResolver Function that will resolve paths to imported file
+ * @returns {String} Content of compiled file
+ */
+function inlineCSSFile(file, pathResolver, keepImports, alreadyImported) {
+ keepImports = keepImports || [];
+ alreadyImported = alreadyImported || {};
+ alreadyImported[file] = true;
+
+ var originalFile = fs.readFileSync(file, 'utf8');
+ var imports = findImports(originalFile);
+ if (!imports.length) {
+ return originalFile;
+ }
+
+ var replacements = [];
+ var reExternal = /^\w+\:\/\//;
+ imports.forEach(function(imp) {
+ var fullPath = pathResolver(imp.file, file);
+ var replaceValue = '';
+
+ if (reExternal.test(imp.file)) {
+ keepImports.push(imp);
+ } else if (!(fullPath in alreadyImported)) {
+ alreadyImported[fullPath] = true;
+ try {
+ replaceValue = inlineCSSFile(fullPath, pathResolver, keepImports, alreadyImported);
+ } catch (e) {
+ throw 'Unable to read "' + imp.file + '" import in ' + file;
+ }
+ }
+
+ replacements.push({
+ start: imp.start,
+ end: imp.end,
+ value: replaceValue
+ });
+ });
+
+ // actually replace imports
+ while (replacements.length) {
+ var r = replacements.pop();
+ originalFile = originalFile.substring(0, r.start) + r.value + originalFile.substring(r.end);
+ }
+
+ return originalFile;
+}
+
+/**
+ * Compiles singe CSS file: concats all @imported file into singe one
+ * @param {String} file Absolute path to CSS file
+ * @param {Function} pathResolver Function that will resolve paths to imported file
+ * @returns {String} Content of compiled file
+ */
+function compileCSSFile(file, pathResolver, alreadyImported) {
+ var keepImports = [];
+ var inlinedContent = inlineCSSFile(file, pathResolver, keepImports, alreadyImported);
+
+ var header = _.map(keepImports, function(imp) {
+ return imp.value;
+ }).join(';\n') + '\n';
+
+ return csso.justDoIt(header + inlinedContent, true);
+}
+
+exports.compileCSSFile = compileCSSFile;
Oops, something went wrong.

0 comments on commit 6744b8d

Please sign in to comment.