ifdef-loader
Advanced tools
Comparing version 2.0.3 to 2.1.0
{ | ||
"name": "ifdef-loader", | ||
"version": "2.0.3", | ||
"version": "2.1.0", | ||
"description": "", | ||
@@ -21,2 +21,6 @@ "main": "ifdef-loader.js", | ||
"email": "erez@erez.pw" | ||
}, | ||
{ | ||
"name": "Wisam Ghantous", | ||
"url": "https://github.com/gwigwam" | ||
} | ||
@@ -23,0 +27,0 @@ ], |
@@ -8,18 +8,22 @@ "use strict"; | ||
useTripleSlash = tripleSlash; | ||
var lines = source.split('\n'); | ||
for (var n = 0;;) { | ||
var startInfo = find_start_if(lines, n); | ||
var pos = 0; | ||
var lines = source; | ||
while (true) { | ||
var startInfo = find_start_if(lines, pos); | ||
if (startInfo === undefined) | ||
break; | ||
var endLine = find_end(lines, startInfo.line); | ||
pos = startInfo.end + 1; | ||
var endLine = find_end(lines, pos); | ||
if (endLine === -1) { | ||
throw "#if without #endif in line " + (startInfo.line + 1); | ||
throw "#if without #endif in line " + positionToLine(lines, startInfo.start); | ||
} | ||
var elseLine = find_else(lines, startInfo.line, endLine); | ||
var elseLine = find_else(lines, pos, endLine); | ||
var cond = evaluate(startInfo.condition, startInfo.keyword, defs); | ||
var fromLine = positionToLine(lines, startInfo.start); | ||
var toLine = positionToLine(lines, endLine.start); | ||
if (cond) { | ||
if (verbose) { | ||
console.log("matched condition #" + startInfo.keyword + " " + startInfo.condition + " => including lines [" + (startInfo.line + 1) + "-" + (endLine + 1) + "]"); | ||
console.log("matched condition #" + startInfo.keyword + " " + startInfo.condition + " => including lines [" + fromLine + "-" + toLine + "]"); | ||
} | ||
blank_code(lines, startInfo.line, startInfo.line); | ||
blank_code(lines, startInfo.start, startInfo.end); | ||
if (elseLine === -1) { | ||
@@ -41,8 +45,8 @@ blank_code(lines, endLine, endLine); | ||
if (verbose) { | ||
console.log("not matched condition #" + startInfo.keyword + " " + startInfo.condition + " => excluding lines [" + (startInfo.line + 1) + "-" + (endLine + 1) + "]"); | ||
console.log("not matched condition #" + startInfo.keyword + " " + startInfo.condition + " => excluding lines [" + fromLine + "-" + toLine + "]"); | ||
} | ||
} | ||
n = startInfo.line; | ||
pos = endLine.end; | ||
} | ||
return lines.join('\n'); | ||
return lines; | ||
} | ||
@@ -152,1 +156,12 @@ exports.parse = parse; | ||
} | ||
function writeChar(s, pos, ch) { | ||
if (pos < 0 || pos > s.length - 1) | ||
throw "out of string boundaries"; | ||
var leftPart = s.substr(0, pos); | ||
var rightPart = s.substr(pos + 1); | ||
var charAt = s.charAt(pos); | ||
if (charAt === '\r' || charAt === '\n' || charAt === '\t') | ||
return s; | ||
return leftPart + ch + rightPart; | ||
} | ||
exports.writeChar = writeChar; |
@@ -1,7 +0,36 @@ | ||
interface IStart { | ||
line: number; | ||
keyword: string; | ||
condition: string; | ||
/** Holds the line indexes for a complete #if block */ | ||
class IfBlock { | ||
/** | ||
* @param startIx Line index of #if | ||
* @param endIx Line index of #endif | ||
* @param elifIxs Line indexes of #elifs | ||
* @param elseIx Line index of #else, or null | ||
* @param innerIfs List of any IfBlocks that are contained within this IfBlock | ||
*/ | ||
constructor(public startIx: number, public endIx: number, public elifIxs: number[] = [], public elseIx: number|null = null, public innerIfs: IfBlock[] = []) { } | ||
getIfRange(): [number, number] { | ||
const to = this.elifIxs.length > 0 ? this.elifIxs[0] : this.elseIx != null ? this.elseIx : this.endIx; | ||
return [this.startIx, to]; | ||
} | ||
getElifRange(index: number): [number, number] { | ||
if(this.elifIxs.length > index) { | ||
const from = this.elifIxs[index]; | ||
const to = this.elifIxs.length > index + 1 ? this.elifIxs[index + 1] : this.elseIx != null ? this.elseIx : this.endIx; | ||
return [from, to]; | ||
} else { | ||
throw `Invalid elif index '${index}', there are only ${this.elifIxs.length} elifs`; | ||
} | ||
} | ||
getElseRange(): [number, number] { | ||
if(this.elseIx != null) { | ||
return [this.elseIx, this.endIx]; | ||
} else { | ||
throw 'Cannot use elseRange when elseIx is null'; | ||
} | ||
} | ||
} | ||
enum IfType { If, Elif } | ||
let useTripleSlash: boolean|undefined; | ||
@@ -15,54 +44,88 @@ | ||
for(let n=0;;) { | ||
let startInfo = find_start_if(lines,n); | ||
if(startInfo === undefined) break; | ||
var ifBlocks = find_if_blocks(lines); | ||
for(let ifBlock of ifBlocks) { | ||
apply_if(lines, ifBlock, defs, verbose); | ||
} | ||
const endLine = find_end(lines, startInfo.line); | ||
if(endLine === -1) { | ||
throw `#if without #endif in line ${startInfo.line+1}`; | ||
return lines.join('\n'); | ||
} | ||
function find_if_blocks(lines: string[]): IfBlock[] { | ||
const blocks: IfBlock[] = []; | ||
for(let i = 0; i < lines.length; i++) { | ||
if(match_if(lines[i])) { | ||
const ifBlock = parse_if_block(lines, i); | ||
blocks.push(ifBlock); | ||
i = ifBlock.endIx; | ||
} | ||
} | ||
return blocks; | ||
} | ||
const elseLine = find_else(lines, startInfo.line, endLine); | ||
/** | ||
* Parse #if statement at given locatoin | ||
* @param ifBlockStart Line on which the '#if' is located. (Given line MUST be start of an if-block) | ||
*/ | ||
function parse_if_block(lines: string[], ifBlockStart: number): IfBlock { | ||
let foundElifs: number[] = []; | ||
let foundElse: number|null = null; | ||
let foundEnd: number|undefined; | ||
let innerIfs: IfBlock[] = []; | ||
const cond = evaluate(startInfo.condition, startInfo.keyword, defs); | ||
for(let i = ifBlockStart + 1; i < lines.length; i++) { | ||
const curLine = lines[i]; | ||
if(cond) { | ||
if(verbose) { | ||
console.log(`matched condition #${startInfo.keyword} ${startInfo.condition} => including lines [${startInfo.line+1}-${endLine+1}]`); | ||
} | ||
blank_code(lines, startInfo.line, startInfo.line); | ||
if (elseLine === -1) { | ||
blank_code(lines, endLine, endLine); | ||
} else { | ||
blank_code(lines, elseLine, endLine); | ||
} | ||
} else { | ||
if (elseLine === -1) { | ||
blank_code(lines, startInfo.line, endLine); | ||
} else { | ||
blank_code(lines, startInfo.line, elseLine); | ||
blank_code(lines, endLine, endLine); | ||
} | ||
if(verbose) { | ||
console.log(`not matched condition #${startInfo.keyword} ${startInfo.condition} => excluding lines [${startInfo.line+1}-${endLine+1}]`); | ||
} | ||
const innerIfMatch = match_if(curLine); | ||
if(innerIfMatch) { | ||
const innerIf = parse_if_block(lines, i); | ||
innerIfs.push(innerIf); | ||
i = innerIf.endIx; | ||
continue; | ||
} | ||
n = startInfo.line; | ||
const elifMatch = match_if(curLine, IfType.Elif); | ||
if(elifMatch) { | ||
foundElifs.push(i); | ||
continue; | ||
} | ||
const elseMatch = match_else(curLine); | ||
if(elseMatch) { | ||
foundElse = i; | ||
continue; | ||
} | ||
const endMatch = match_endif(curLine); | ||
if(endMatch) { | ||
foundEnd = i; | ||
break; | ||
} | ||
} | ||
return lines.join('\n'); | ||
if(foundEnd === undefined) { | ||
throw `#if without #endif on line ${ifBlockStart + 1}`; | ||
} | ||
return new IfBlock(ifBlockStart, foundEnd, foundElifs, foundElse, innerIfs); | ||
} | ||
function match_if(line: string): IStart|undefined { | ||
const re = useTripleSlash ? /^[\s]*\/\/\/([\s]*)#(if)([\s\S]+)$/g : /^[\s]*\/\/([\s]*)#(if)([\s\S]+)$/g; | ||
const ifRegex = () => useTripleSlash ? /^[\s]*\/\/\/([\s]*)#(if|elif)([\s\S]+)$/g : /^[\s]*\/\/([\s]*)#(if|elif)([\s\S]+)$/g; | ||
function match_if(line: string, type: IfType = IfType.If) : boolean { | ||
const re = ifRegex(); | ||
const match = re.exec(line); | ||
return match !== null && ((type == IfType.If && match[2] == "if") || (type == IfType.Elif && match[2] == "elif")); | ||
} | ||
/** | ||
* @param line Line to parse, must be a valid #if statement | ||
* @returns The if condition | ||
*/ | ||
function parse_if(line: string): string { | ||
const re = ifRegex(); | ||
const match = re.exec(line); | ||
if(match) { | ||
return { | ||
line: -1, | ||
keyword: match[2], | ||
condition: match[3].trim() | ||
}; | ||
return match[3].trim(); | ||
} else { | ||
throw `Could not parse #if: '${line}'`; | ||
} | ||
return undefined; | ||
} | ||
@@ -82,54 +145,51 @@ | ||
function find_start_if(lines: string[], n: number): IStart|undefined { | ||
for(let t=n; t<lines.length; t++) { | ||
const match = match_if(lines[t]); | ||
if(match !== undefined) { | ||
match.line = t; | ||
return match; | ||
// TODO: when es7 write as: return { line: t, ...match }; | ||
} | ||
} | ||
return undefined; | ||
} | ||
/** Includes and excludes relevant lines based on evaluation of the provided IfBlock */ | ||
function apply_if(lines: string[], ifBlock: IfBlock, defs: object, verbose: boolean = false) { | ||
let includeRange: [number, number]|null = null; | ||
function find_end(lines: string[], start: number): number { | ||
let level = 1; | ||
for(let t=start+1; t<lines.length; t++) { | ||
const mif = match_if(lines[t]); | ||
const mend = match_endif(lines[t]); | ||
const ifCond = parse_if(lines[ifBlock.startIx]); | ||
const ifRes = evaluate(ifCond, defs); | ||
if(mif) { | ||
level++; | ||
const log = (condition: string, outcome: boolean) => { | ||
if(verbose) { | ||
console.log(`#if block lines [${ifBlock.startIx + 1}-${ifBlock.endIx + 1}]: Condition '${condition}' is ${outcome ? 'TRUE' : 'FALSE'}. ${includeRange != null ? `Including lines [${includeRange[0] + 1}-${includeRange[1] + 1}].` : 'Excluding everything.'}`); | ||
} | ||
}; | ||
if(mend) { | ||
level--; | ||
if(level === 0) { | ||
return t; | ||
if(ifRes) { | ||
includeRange = ifBlock.getIfRange(); | ||
log(ifCond, true); | ||
} else { | ||
for(let elifIx = 0; elifIx < ifBlock.elifIxs.length; elifIx++) { | ||
const elifLine = lines[ifBlock.elifIxs[elifIx]]; | ||
const elifCond = parse_if(elifLine); | ||
const elifRes = evaluate(elifCond, defs); | ||
if(elifRes) { | ||
includeRange = ifBlock.getElifRange(elifIx); | ||
log(elifCond, true); | ||
break; | ||
} | ||
} | ||
} | ||
return -1; | ||
} | ||
function find_else(lines: string[], start: number, end: number): number { | ||
let level = 1; | ||
for(let t=start+1; t<end; t++) { | ||
const mif = match_if(lines[t]); | ||
const melse = match_else(lines[t]); | ||
const mend = match_endif(lines[t]); | ||
if(mif) { | ||
level++; | ||
if(includeRange == null) { | ||
if(ifBlock.elseIx != null) { | ||
includeRange = ifBlock.getElseRange(); | ||
} | ||
log(ifCond, false); | ||
} | ||
} | ||
if(mend) { | ||
level--; | ||
} | ||
if(includeRange != null) { | ||
blank_code(lines, ifBlock.startIx, includeRange[0]); | ||
blank_code(lines, includeRange[1], ifBlock.endIx); | ||
} else { | ||
blank_code(lines, ifBlock.startIx, ifBlock.endIx); | ||
} | ||
if (melse && level === 1) { | ||
return t; | ||
for(let innerIf of ifBlock.innerIfs) { | ||
// Apply inner-if blocks only when they are not already erased | ||
if(includeRange != null && innerIf.startIx >= includeRange[0] && innerIf.startIx <= includeRange[1]) { | ||
apply_if(lines, innerIf, defs, verbose); | ||
} | ||
} | ||
return -1; | ||
} | ||
@@ -140,4 +200,3 @@ | ||
*/ | ||
function evaluate(condition: string, keyword: string, defs: object): boolean { | ||
function evaluate(condition: string, defs: object): boolean { | ||
const code = `return (${condition}) ? true : false;`; | ||
@@ -156,10 +215,6 @@ const args = Object.keys(defs); | ||
if(keyword === "ifndef") { | ||
result = !result; | ||
} | ||
return result; | ||
} | ||
function blank_code(lines: string[], start: number, end: number) { | ||
function blank_code(lines: string[], start: number, end: number = start) { | ||
for(let t=start; t<=end; t++) { | ||
@@ -166,0 +221,0 @@ const len = lines[t].length; |
# ifdef-loader | ||
Webpack loader that allows JavaScript or TypeScript conditional compilation (`#if ... #else ... #endif`) | ||
Webpack loader that allows JavaScript or TypeScript conditional compilation (`#if ... #elif ... #else ... #endif`) | ||
directly from Webpack. | ||
@@ -24,5 +24,15 @@ | ||
If the expression is `true` the block of code between `#if` and `#endif` is included, | ||
otherwise is excluded by commenting it out. | ||
If the expression is `true` the block of code between `#if` and `#endif` is included, otherwise is excluded by commenting it out. | ||
Additionally, `#elif` and `#else` clauses can be added to an `#if` clause: | ||
```js | ||
/// #if env == 'PRODUCTION' | ||
console.log('Production!'); | ||
/// #elif env == 'DEBUG' | ||
console.log('Debug!'); | ||
/// #else | ||
console.log('Something else!'); | ||
/// #endif | ||
``` | ||
The `#if` clauses can also be nested: | ||
@@ -33,4 +43,3 @@ ```js | ||
android_code(); | ||
/// #endif | ||
/// #if OS=="ios" | ||
/// #elif OS=="ios" | ||
ios_code(); | ||
@@ -41,11 +50,2 @@ /// #endif | ||
Additionally, `#else` clauses can be defined for every `#if` clause: | ||
```js | ||
/// #if PRODUCTION | ||
console.log('Production!'); | ||
/// #else | ||
console.log('Something else!'); | ||
/// #endif | ||
``` | ||
## Installation | ||
@@ -83,3 +83,3 @@ | ||
// alternatively, options can be passed via query string: | ||
const q = require('querystring').encode(opts}); | ||
const q = require('querystring').encode(opts); | ||
/* ... */ { | ||
@@ -86,0 +86,0 @@ test: /\.tsx?$/, |
// tests again lines made of just one sigle char (https://github.com/nippur72/ifdef-loader/pull/2) | ||
///////////// | ||
/////////////// | ||
//////// | ||
/// #if false | ||
function foo(){ | ||
// ... | ||
////////// | ||
} | ||
/// #endif |
// tests again lines made of just one sigle char (https://github.com/nippur72/ifdef-loader/pull/2) | ||
///////////// | ||
/////////////// | ||
//////// | ||
/// #if false | ||
function foo(){ | ||
// ... | ||
////////// | ||
} | ||
/// #endif |
var a=1; | ||
/////////////////// | ||
/////////////// | ||
///////////////////////////// | ||
///////////////////////////////// | ||
/////////////// | ||
///////// | ||
/// #if version > 4 | ||
loadVersion4(); | ||
/// #if OS === "android" | ||
android_Init_In_Version_4(); | ||
/// #endif | ||
/// #else | ||
loadVersion2(); | ||
////////// | ||
/// #endif | ||
a=a+1; | ||
var b=1; | ||
/////////////////// | ||
/// #if version < 4 | ||
loadVersion2(); | ||
///////////////////////// | ||
///////////////////////////// | ||
////////////// | ||
/// #if OS === "ios" | ||
IOS_Init_In_Version_2(); | ||
/// #else | ||
Init_General(); | ||
/////////////// | ||
///////// | ||
///////////// | ||
////////// | ||
/// #endif | ||
/// #else | ||
wontHappen(); | ||
/// #endif | ||
b=a+1; |
var a=1; | ||
/////////////////// | ||
/////////////// | ||
///////////////////////////// | ||
///////////////////////////////// | ||
/////////////// | ||
///////// | ||
/// #if version > 4 | ||
loadVersion4(); | ||
/// #if OS === "android" | ||
android_Init_In_Version_4(); | ||
/// #endif | ||
/// #else | ||
loadVersion2(); | ||
////////// | ||
/// #endif | ||
a=a+1; | ||
var b=1; | ||
/////////////////// | ||
/// #if version < 4 | ||
loadVersion2(); | ||
///////////////////////// | ||
///////////////////////////// | ||
////////////// | ||
/// #if OS === "ios" | ||
IOS_Init_In_Version_2(); | ||
/// #else | ||
Init_General(); | ||
/////////////// | ||
///////// | ||
///////////// | ||
////////// | ||
/// #endif | ||
/// #else | ||
wontHappen(); | ||
/// #endif | ||
b=a+1; |
var a=1; | ||
/////////////////// | ||
/////////////// | ||
///////////////////////////// | ||
///////////////////////////////// | ||
/////////////// | ||
////////// | ||
/// #if version > 4 | ||
loadVersion4(); | ||
/// #if OS === "android" | ||
android_Init_In_Version_4(); | ||
/// #endif | ||
/// #endif | ||
a=a+1; | ||
var b=1; | ||
/////////////////// | ||
/// #if version < 4 | ||
loadVersion2(); | ||
///////////////////////// | ||
///////////////////////////// | ||
/////////////// | ||
////////// | ||
/// #if OS === "ios" | ||
IOS_Init_In_Version_2(); | ||
/// #endif | ||
/// #endif | ||
b=a+1; |
var a=1; | ||
/////////////////// | ||
/////////////// | ||
///////////////////////////// | ||
///////////////////////////////// | ||
/////////////// | ||
////////// | ||
/// #if version > 4 | ||
loadVersion4(); | ||
/// #if OS === "android" | ||
android_Init_In_Version_4(); | ||
/// #endif | ||
/// #endif | ||
a=a+1; | ||
var b=1; | ||
/////////////////// | ||
/// #if version < 4 | ||
loadVersion2(); | ||
///////////////////////// | ||
///////////////////////////// | ||
/////////////// | ||
////////// | ||
/// #if OS === "ios" | ||
IOS_Init_In_Version_2(); | ||
/// #endif | ||
/// #endif | ||
b=a+1; |
@@ -1,24 +0,18 @@ | ||
export default function test() { | ||
////////////////////////// | ||
////////////////////////// | ||
//////////// | ||
} | ||
var a=1; | ||
//////////// | ||
// #if DEBUG | ||
debug("hello"); | ||
///////// | ||
// #endif | ||
var b=2; | ||
/////////////////////// | ||
// #if OS === "android" | ||
androidInit(); | ||
///////// | ||
// #endif | ||
var c=3; | ||
////////////////// | ||
/////////////// | ||
///////// | ||
///////////// | ||
/////////////// | ||
//////// | ||
// #if version > 4 | ||
loadVersion4(); | ||
// #endif | ||
// #if !DEBUG | ||
debug("hello"); | ||
// #else | ||
debug("or not"); | ||
///////// | ||
// #endif | ||
@@ -25,0 +19,0 @@ /***************** */ |
@@ -1,24 +0,18 @@ | ||
export default function test() { | ||
////////////////////////// | ||
////////////////////////// | ||
//////////// | ||
} | ||
var a=1; | ||
//////////// | ||
// #if DEBUG | ||
debug("hello"); | ||
///////// | ||
// #endif | ||
var b=2; | ||
/////////////////////// | ||
// #if OS === "android" | ||
androidInit(); | ||
///////// | ||
// #endif | ||
var c=3; | ||
////////////////// | ||
/////////////// | ||
///////// | ||
///////////// | ||
/////////////// | ||
//////// | ||
// #if version > 4 | ||
loadVersion4(); | ||
// #endif | ||
// #if !DEBUG | ||
debug("hello"); | ||
// #else | ||
debug("or not"); | ||
///////// | ||
// #endif | ||
@@ -25,0 +19,0 @@ /***************** */ |
var a=1; | ||
///////////// | ||
/// #if DEBUG | ||
debug("hello"); | ||
////////// | ||
/// #endif | ||
var b=2; | ||
//////////////////////// | ||
/// #if OS === "android" | ||
androidInit(); | ||
////////// | ||
/// #endif | ||
var c=3; | ||
/////////////////// | ||
/////////////// | ||
////////// | ||
////////////// | ||
/////////////// | ||
///////// | ||
/// #if version > 4 | ||
loadVersion4(); | ||
/// #endif | ||
/// #if !DEBUG | ||
debug("hello"); | ||
/// #else | ||
debug("or not"); | ||
////////// | ||
/// #endif | ||
var a=1; | ||
/* #if DEBUG */ | ||
debug("hello"); | ||
/* #endif */ | ||
var b=2; | ||
/* #if OS === "android" */ | ||
androidInit(); | ||
/* #endif */ | ||
var c=3; | ||
/* #if version > 4 */ | ||
loadVersion4(); | ||
/* #endif */ | ||
/* #if !DEBUG */ | ||
debug("hello"); | ||
/* #else */ | ||
debug("or not"); | ||
/* #endif */ |
var a=1; | ||
///////////// | ||
/// #if DEBUG | ||
debug("hello"); | ||
////////// | ||
/// #endif | ||
var b=2; | ||
//////////////////////// | ||
/// #if OS === "android" | ||
androidInit(); | ||
////////// | ||
/// #endif | ||
var c=3; | ||
/////////////////// | ||
/////////////// | ||
////////// | ||
////////////// | ||
/////////////// | ||
///////// | ||
/// #if version > 4 | ||
loadVersion4(); | ||
/// #endif | ||
/// #if !DEBUG | ||
debug("hello"); | ||
/// #else | ||
debug("or not"); | ||
////////// | ||
/// #endif | ||
var a=1; | ||
/* #if DEBUG */ | ||
debug("hello"); | ||
/* #endif */ | ||
var b=2; | ||
/* #if OS === "android" */ | ||
androidInit(); | ||
/* #endif */ | ||
var c=3; | ||
/* #if version > 4 */ | ||
loadVersion4(); | ||
/* #endif */ | ||
/* #if !DEBUG */ | ||
debug("hello"); | ||
/* #else */ | ||
debug("or not"); | ||
/* #endif */ |
@@ -0,0 +0,0 @@ (function(e, a) { for(var i in a) e[i] = a[i]; }(this, /******/ (function(modules) { // webpackBootstrap |
@@ -20,3 +20,3 @@ "use strict"; | ||
}; | ||
describe("files spec", function () { | ||
xdescribe("files spec", function () { | ||
var files = ["simple", "nested", "dfleury", "nested.else", "simple.doubleslash"]; | ||
@@ -52,3 +52,3 @@ var fileSet = files.map(function (fn) { return ({ | ||
}); | ||
describe("webpack bundle", function () { | ||
xdescribe("webpack bundle", function () { | ||
var files = ["webpack"]; | ||
@@ -55,0 +55,0 @@ var fileSet = files.map(function (fn) { return ({ |
@@ -28,3 +28,3 @@ /// <reference types="jasmine" /> | ||
const files = [ "simple", "nested", "dfleury", "nested.else", "simple.doubleslash" ]; | ||
const files = [ "simple", "nested", "dfleury", "nested.else", "simple.doubleslash", "elif", "nested.elif" ]; | ||
@@ -31,0 +31,0 @@ const fileSet = files.map(fn => ({ |
44396
44
1421