mirror of
https://github.com/scratchfoundation/paper.js.git
synced 2025-01-01 02:38:43 -05:00
194 lines
5.5 KiB
JavaScript
Executable file
194 lines
5.5 KiB
JavaScript
Executable file
#! /usr/bin/env node
|
|
/*
|
|
* Paper.js - The Swiss Army Knife of Vector Graphics Scripting.
|
|
* http://paperjs.org/
|
|
*
|
|
* Copyright (c) 2011 - 2013, 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.
|
|
*
|
|
* Arguments:
|
|
* -d DEFINE_JSON -- define a json containing defintions availabe to prepro
|
|
* -i INCLUDE_JS -- include a JS file containing definitinos availabe to prepro
|
|
* -c -- strip comments
|
|
*/
|
|
|
|
// Required libs
|
|
|
|
var fs = require('fs'),
|
|
path = require('path'),
|
|
vm = require('vm');
|
|
|
|
// Preprocessing
|
|
|
|
var code = [];
|
|
|
|
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:
|
|
// /*#=*/ eval (outside comments)
|
|
// *#=* eval (inside comments)
|
|
line = line.replace(/\/?\*#=\*\/?\s*([\w.]*)/g,
|
|
function(all, val) {
|
|
return eval(val);
|
|
}
|
|
);
|
|
// Now add a statement that when evaluated writes out this code line
|
|
code.push('out.push(' + JSON.stringify(line) + ');');
|
|
}
|
|
});
|
|
}
|
|
|
|
function parse() {
|
|
var out = [];
|
|
// Evaluate the collected code: Collects result in out, through out.push()
|
|
eval(code.join('\n'));
|
|
// Start again with a new code buffer.
|
|
code = [];
|
|
// Return the resulting lines as one string.
|
|
return out.join('\n');
|
|
}
|
|
|
|
// 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 '-i':
|
|
// Include code to be present at prepro time, e.g. for on-the-fly
|
|
// replacement of constants, using /*#=*/ statements.
|
|
// Luckily we can reuse the include() / parse() functionality to do so:
|
|
var file = args.shift();
|
|
if (file) {
|
|
include(path.resolve(), path.normalize(file));
|
|
eval(parse());
|
|
}
|
|
break;
|
|
case '-c':
|
|
strip = true;
|
|
break;
|
|
default:
|
|
files.push(arg);
|
|
}
|
|
}
|
|
|
|
// Include all files. Everything else happens from there, through include()
|
|
files.forEach(function(file) {
|
|
include(path.resolve(), file);
|
|
});
|
|
|
|
var out = parse();
|
|
|
|
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) {
|
|
// Add some padding so we can always look ahead and behind by two chars
|
|
str = ('__' + str + '__').split('');
|
|
var quote = false,
|
|
quoteSign,
|
|
blockComment = false,
|
|
lineComment = false,
|
|
preserveComment = false;
|
|
for (var i = 0, l = str.length; i < l; i++) {
|
|
if (quote) {
|
|
// When checking for quote escaping, we also need to check that the
|
|
// escape sign itself is not escaped, as otherwise '\\' would cause
|
|
// the wrong impression of an unclosed string:
|
|
if (str[i] === quoteSign && (str[i - 1] !== '\\' || str[i - 2] === '\\'))
|
|
quote = 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 (/[\n\r]/.test(str[i + 1]))
|
|
lineComment = false;
|
|
str[i] = '';
|
|
} else {
|
|
quote = /['"]/.test(str[i]);
|
|
if (quote)
|
|
quoteSign = str[i];
|
|
if (!blockComment && str[i] === '/') {
|
|
if (str[i + 1] === '*') {
|
|
// Do not filter out conditional comments /*@ ... */
|
|
// and comments marked as protected /*! ... */
|
|
preserveComment = /[@!]/.test(str[i + 2]);
|
|
if (!preserveComment)
|
|
str[i] = '';
|
|
blockComment = true;
|
|
} else if (str[i + 1] === '/') {
|
|
str[i] = '';
|
|
lineComment = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
// Remove padding again.
|
|
return str.join('').slice(2, -2);
|
|
}
|