paper.js/build/prepro.js

174 lines
4.8 KiB
JavaScript
Executable file

#! /usr/bin/env node
/*
* Paper.js
*
* This file is part of Paper.js, a JavaScript Vector Graphics Library,
* based on Scriptographer.org and designed to be largely API compatible.
* http://paperjs.org/
* http://scriptographer.org/
*
* Copyright (c) 2011, Juerg Lehni & Jonathan Puckey
* http://lehni.org/ & http://jonathanpuckey.com/
*
* Distributed under the MIT license. See LICENSE file for details.
*
* All rights reserved.
*/
/**
* Prepro.js - A simple preprocesssor for JavaScript that speaks JavaScript,
* written in JavaScript, allowing preprocessing to either happen at build time
* or compile time. Very useful for libraries that are built for distribution,
* but can be also compiled from seperate sources directly for development,
* supporting build time switches.
*/
// Required libs
var fs = require('fs'),
path = require('path');
// Parse arguments
var args = process.argv.slice(2),
options = {},
files = [],
strip = false;
while (args.length > 0) {
var arg = args.shift();
switch (arg) {
case '-d':
// Definitions are provided as JSON and supposed to be object literals
var def = JSON.parse(args.shift());
// Merge new definitions into options object.
for (var key in def)
options[key] = def[key];
break;
case '-c':
strip = true;
break;
default:
files.push(arg);
}
}
// Preprocessing
var code = [],
out = [];
function include(base, file) {
// Compose a pathname from base and file, which is specified relatively,
// and normalize the new path, to get rid of ..
file = path.normalize(path.join(base, file));
var content = fs.readFileSync(file).toString();
content.split(/\r\n|\n|\r/mg).forEach(function(line) {
// See if our line starts with the preprocess prefix.
var match = line.match(/^\s*\/\*#\*\/\s*(.*)$/);
if (match) {
// Check if the preprocessing line is an include statement, and if
// so, handle it straight away
line = match[1];
if (match = line.match(/^include\(['"]([^;]*)['"]\);?$/)) {
// Pass on the dirname of the current file as the new base
include(path.dirname(file), match[1]);
} else {
// Any other preprocessing code is simply added, for later
// evaluation.
code.push(line);
}
} else {
// Perhaps we need to replace some values? Supported formats are:
// /*#=*/ options.NAME (outside comments)
// *#=* options.NAME (inside comments)
line = line.replace(/\/?\*#=\*\/?\s*options\.([\w]*)/g,
function(all, name) {
return options[name];
}
);
// Now add a statement that when evaluated writes out this code line
code.push('out.push(' + JSON.stringify(line) + ');');
}
});
}
// Include all files. Everything else happens from there, through include()
files.forEach(function(file) {
include(path.resolve(), file);
});
// Evaluate the resulting code: Calls puts() and writes the result to stdout.
eval(code.join('\n'));
// Convert the resulting lines to one string again.
var out = out.join('\n');
if (strip) {
out = stripComments(out);
// Strip empty lines that contain only white space and line breaks, as they
// are left-overs from comment removal.
out = out.replace(/^[ \t]+(\r\n|\n|\r)/gm, function(all) {
return '';
});
// Replace a sequence of more than two line breaks with only two.
out = out.replace(/(\r\n|\n|\r)(\r\n|\n|\r)+/g, function(all, lineBreak) {
return lineBreak + lineBreak;
});
}
// Write the result out
process.stdout.write(out);
/**
* Strips comments out of JavaScript code, based on:
* http://james.padolsey.com/javascript/removing-comments-in-javascript/
*/
function stripComments(str) {
str = ('__' + str + '__').split('');
var singleQuote = false,
doubleQuote = false,
blockComment = false,
lineComment = false,
preserveComment = false;
for (var i = 0, l = str.length; i < l; i++) {
if (singleQuote) {
if (str[i] == "'" && str[i - 1] !== '\\')
singleQuote = false;
} else if (doubleQuote) {
if (str[i] == '"' && str[i - 1] !== '\\')
doubleQuote = false;
} else if (blockComment) {
// Is the block comment closing?
if (str[i] == '*' && str[i + 1] == '/') {
if (!preserveComment)
str[i] = str[i + 1] = '';
blockComment = preserveComment = false;
} else if (!preserveComment) {
str[i] = '';
}
} else if (lineComment) {
// One-line comments end with the line-break
if (str[i + 1] == '\n' || str[i + 1] == '\r')
lineComment = false;
str[i] = '';
} else {
doubleQuote = str[i] == '"';
singleQuote = str[i] == "'";
if (!blockComment && str[i] == '/') {
if (str[i + 1] == '*') {
// Do not filter out conditional comments and comments marked
// as protected (/*! */)
preserveComment = str[i + 2] == '@' || str[i + 2] == '!';
if (!preserveComment)
str[i] = '';
blockComment = true;
} else if (str[i + 1] == '/') {
str[i] = '';
lineComment = true;
}
}
}
}
return str.join('').slice(2, -2);
}