forked from paperjs/paper.js
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathprepro.js
executable file
·174 lines (159 loc) · 4.85 KB
/
prepro.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
#! /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);
}