jspreproc follows the C preprocessor style, with almost all the same keywords, preceded by //
However, there are some differences.
Differences from the C preprocessor:
- The
#define
keyword does not works in jspreproc as in the C preprocessor and is deprecated in 0.2.3 -- Use the#set
keyword instead - jspreproc does not supports function-like macros or macro-substitution on the source files, out of expressions(1)
- Escaped characters remains literals, e.g.
\n
does not generate an end of line, it is written to the output as the string "\n" - The evaluation of expressions by
#set/#if/#elif
is done through a Function instance, so the result is the same as for a JavaScript expression in the global scope.
(1) See Variables Symbols for some exceptions.
For jspreproc recognize the directives, keep them in its own line, with no other tokens except single-line comments following the directive. Only spaces and tabs are allowed before the starting //#
, and between this and the keyword.
jspreproc keywords are case sensitive.
So, this is valid:
//# if FOO
//#if FOO
But this is incorrect:
// #if FOO // <-- invalid separation between "//" and "#"
//#If FOO // <-- case-sensitive "If" is not "if"
If the sequence of directives are malformed, as the unclosed blocks in the above examples, jspreproc emits an error event on the output stream.
These looks similar to C preprocessor #define
's, but its behavior is the same as JavaScript variables.
//#set SYMBOL [=] expression
The sign =
before the expression is optional.
Once defined, the symbol is global to all files and its value can be changed at any time.
Valid names are all uppercase, starts with one character in the range [$_A-Z]
, followed by one or more of [0-9_A-Z]
, so minimum length is 2.
__FILE
is the only predefined symbol, contains the relative name of the file being processed.
You can use existing symbols in a new definition. First, any symbols (__FILE
inclusive) are replaced with its current value, or zero for nonexistent symbols. Last, the expression is evaluated and its result assigned.
jspreproc supports, in experimental fashion, macro-expansion in the source file for names beginning with $_
, followed by one or more characters in [0-9_A-Z]
, so their minimum length is 3. The replacement is a literal substitution with String.prototype.replace
and the raw value of the symbol at the time of replacement.
This is an example of the process:
//#set $_FOO = 'fo'+'o' // $_FOO is evaluated to 'foo'
//#set $_BAR = "bar" // $_BAR is evaluated to 'bar'
//#set $_BAZ = $_FOO + $_BAR // evaluates to 'foobar'
//#unset $_FOO // dees not affect $_BAZ
//#set $_BAR = "?" // $_BAZ remains the same
console.log($_BAZ) // outputs "foobar"
//#unset SYMBOL
Removes a previously defined symbol.
Conditional Comments allows remove unused parts and build different versions of your application.
//#if expression
//#elif expression
If the expression evaluates to falsy, the block following the statement is removed.
jspreproc supports the function defined(SYMBOL)
, wich returns 1 if SYMBOL
was defined, even when this has the undefined
value :)
//#ifdef SYMBOL
//#ifndef SYMBOL
Test the existence of a defined symbol.
These are shorthands for #if defined(SYMBOL)
and #if !defined(SYMBOL)
.
//#else
//#endif
These are the default block and closing statement keywords.
From v0.2.7 you can hide conditional blocks using multiline comments. This syntax is valid for the if/ifdef/ifndef/elif/else
keywords only. The opening sequence (/*
) must comply with the same rules as the one-line conditional comments, the closing sequence (*/
) must follow another regular conditional comment:
/*#if EXPRESSION
...
//#endif */
Note the //#endif
, this is a regular conditional comment, but now the parser will ignore anything in the line starting from */
. Tools such as minifiers or syntax highlighters will see these blocks as a regular multiline comment, which allows you to hide the block, as in this example:
//#set FOO = 1
/*#if FOO == 1
var x = one()
//#elif FOO == 2
var x = two()
//#else*/
var x = other()
//#endif
//#include filename
//#include_once filename
These statements inserts the content of another file. filename
can be an absolute file name, or relative to the current file. Default extension is .js
.
You can include the same file multiple times in the current file, but the statement is ignored if the same file was processed or included in a preceding level (avoids recursion).
jspreproc does not cache the file, it is readed from the file system and preprocessed before each insertion.
Use include_once
to limit the inclusion to one copy by mainstream.
//#if DEF == 1 // you can use single-line comments after the expression
console.log('only preserved if DEF is 1')
//#endif
//# if (DEF) // you can insert spaces between `#` and the keyword
//# if FOO // ...but not between the `//` and `#`
//# endif // indented conditional comments are recognized
//# else
//# if BAR
//# endif
//# endif
//#if defined(DEF1) || defined(DEF2) // defined() is supported
console.log('DEF 1 or 2 defined')
//#elif defined(DEF3)
console.log('DEF 3 defined')
//#endif
//#if DEBUG // 0 if DEBUG is not defined
console.log('info')
//#endif
Some variable assignments
//#set FOO "one" // you can omit the equal sign
//#set DEBUG // DEBUG value is 1
//#set THREE = (1+2) // THREE value is 3
//#set TODAY = new Date() // Date instances are preserved
//#set REGEX = /^$/ // so RegExp
//#set TRUE = !0 // and boolean
Using variable symbols in the source code
//#set $_NAME = 1 // must begin with '$_'
var foo = $_NAME + 1 // foo = 2
//#set $_NAME = 'foo' // change defined value
var bar = $_NAME // bar = "foo"
//#set $_NAME = 'foo'+'bar' // concatenation
var baz = $_NAME // baz = "foobar"
//#set $_NAME = /^a/ // define a regex
var a = $_NAME.test('a') // a = /^a/.test('a')
//#unset $_NAME // delete $_NAME
var x = $_NAME // SyntaxError
Effects of immediate evaluation
//#set $_FOO = 'foo' // value defaults to 1
//#set $_BAR = NAME + 'bar' // OTHER value is 'foobar'
//#set $_FOO = 'baz'
console.log('%s %s', $_FOO, $_BAR) // prints 'baz foobar'
//#unset $_FOO
console.log('%s %s', $_FOO, $_BAR) // SyntaxError
// Next assignment sets FILE to the name of the *current* file
//#set FILE '__FILE'
// From now on, although the file being processed change, the value of
// FILE remains the same. You need reassign FILE to update their value.
Fail safe #set
Even without run jspreproc on file2.js, next code will work:
// file1.js
//#set $_NAME = 1
// file2.js
//#include file1
//#ifndef $_NAME
var $_NAME = 1;
//#endif
console.log($_NAME); // prints '1'
Including files
//#include myfile // myfile.js in the same folder of current file
//#include_once ../myone // one copy only
//#include myfile // ok to insert again
//#include ../myone.js // ignored
Quotes are optional, except if the filename include spaces
//#include "file1"
//#include 'file2'
//#include "other file" // quotes are required