-
Notifications
You must be signed in to change notification settings - Fork 9
Syntax
[blanks]<prefixes>#<keyword>[<space>[<expresion>]][//[jscc-comment]]
[blanks]
- Only spaces, tabs, and form-feeds are allowed before the start of the directive.
<prefixes>
- Usually a comment, by default '//'
or '/*'
. Warning: jscc don't know about Multiline Comments.
//#<keyword>
- jscc keyword, with no spaces between the pound sign and comment-start
.
<space>
- Space or tab. Only required if the line has any text following the keyword.
<expression>
- Ignored for else
and endif
, required for others keywords.
[//[comment]]
- The '//'
starts a jscc comment, anything from there is discarded.
Directives must start in its own line and cannot spawn multiple lines, the end of line signals the end of the directive.
The sequence of characters starting jscc directives can be configured.
//#set _DEBUG = 1 // this is a jscc comment
/*#if _DEBUG // closing this multiline comment */
//#if process.env.devmode === 'production'
//#set _DEBUG 0 // the `=` is optional
//#else anything after `#else` or `#endif` is ignored
/* eslint-disable no-console */
console.log('Debug mode on.')
//#endif
//#endif _DEBUG '_DEBUG' is ignored
You open a jscc directive with a (configurable) sequence of characters after the first optional spaces and before the pound sign (#
) of the directive.
It sequence defaults to ['//', '/*']
, that works fine with highlighters of JavaScript-like code (including C#), but you can use this in any source.
The directive ends at the end of the line (Unix, Mac, or Windows).
Once evaluated, the line containing the directive is removed.
You can use multiline comments, but jscc don't know about that, so it is necessary to set the closing sequence in a jscc comment like this (for JS):
/*#if _FOO //*/
This is too verbose, I know (and do not recommended it), but avoids having complex rules and parsers in jscc. Also, allows do some tricks like hide blocks.
Use '_'
followed by one digit or uppercase letter, followed by zero or more underscores, digits or uppercase letters.
Good: '_DEBUG'
, '_1'
, '_TOP_A'
Bad: '__DEBUG'
, '_$1'
, 'TOP_A'
There's two predefined varnames that you can use like any other:
This is the filename of the file being processed, relative to the current working directory, with path separators normalized to '/'
.
Its value is readonly.
You can set the value of this varname through the option.values
passed to the plugin.
If not, this holds the value of version
in the package.json
file found in the current working directory or some level up, read once during plugin initialization.
Its value is not readonly, but is resetted in each file change.
Prefix the varname to be replaced with '$'
.
Think about '$'
as a required "paste" token which allows you join jscc values in your code:
//#set _BAR = 'bar'
//#set _BAZ = 'baz'
let s = 'foo$_BAR$_BAZ'; // s = 'foobarbaz'
To the right, the implicit delimiter is any character except underscore, digits, and letters (i.e. \w
):
The replacement skips non-existent varnames and property identifiers, so this does not works:
// not replaced, 'b' is not a delimiter and jscc see `$_BARbaz` here
let s = 'foo$_BARbaz'
Objects are handled in a special way. If you follow the object's name with an existing property, this is replaced with the property value.
If you omit the property, or not exists, the ouput is the same from JSON.stringify
:
//#set _OBJ = { tag: 1 }
let s = _OBJ.tag // s = 1
let o = _OBJ // o = {"tag":1}
let x = _OBJ.foo // x = {"tag":1}.foo
Only root (first level) properties are recognized and their names cannot contains the character '$'
.
Other object, including arrays, are replaced with their JSON.stringify
result.
Expressions in jscc are simple JavaScript and has simple rules:
- Expressions are evaluated in the context of the jscc variables (shallow copy of
options.values
) - Non-defined varnames are replaced with the
undefined
value - Varnames inside quotes or regexes are not evaluated
- Expressions are not transpiled, use the native JS supported by your environment.
You don't need prefix the varnames with this
as jscc does it for you and if a variable is not in this
but exists in the global
object (at compile-time), the later is used.
Examples:
//#set _FOO = ('foo' + 'bar').toUpperCase()
//#set _BAR = _FOO === 'FOOBAR' ? 'Yes' : 'No'
//#set _BAZ = '_BAR'
console.log('$_BAR') // print `yes`
console.log('$_BAZ') // print `_BAR`
Unlike the C preprocessor, jscc variables are fully dynamics:
//#set _FOO = 1
//#set _BAR = 2
//#set _BAZ = _FOO + _BAR
//---> now, _BAZ value is 3
//#set _FOO = /abc/
//---> _FOO was redefined, _BAZ remains 3
let foo = $_BAZ // foo = 3
let baz = $_FOO.test('abc') // baz = /abc/.test('abc')
This code is ok to jscc, but will generate issues with linters:
//#ifdef _MODULES
export default
//#else
module.exports =
//#endif
function () {
return 'whatever'
}
jscc has a feature that allows you hide one block:
/*#ifdef _MODULES
export default
//#else // */
module.exports =
//#endif
function () {
return 'whatever'
}
Solved.
How works?
The above code works because the predefined opening sequences of directives is //
and /*
, but the only closing sequences is another //
or the end of line.
So, from the jscc perpective, the first /*
becomes the same as //
and the directive ends with the second //
, the staring of the jscc own comment.
From there, all works as expected, if _MODULES
is to trueish, the line with export default
is included, if not, module.exports =
is.
The */
closing the multiline comment is not seen by jscc.
* The space inside "// */"
is for the preview of my editor (VS Code), jscc does not require it.