Skip to content

Commit

Permalink
Merge pull request #7 from HydraWiki/develop
Browse files Browse the repository at this point in the history
Fix issues with @import and boolean variables.
  • Loading branch information
Alexia authored Nov 30, 2018
2 parents 6968c5a + 1dfec62 commit 50cf89c
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 69 deletions.
167 changes: 100 additions & 67 deletions lib/LESSoid.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
* RESToid shim based around MediaWiki's calling of Less_Parser
*/
class Less_Parser {

public static $default_options = [
'compress' => false, // option - whether to compress
'strictUnits' => false, // whether units need to evaluate correctly
Expand All @@ -24,21 +23,33 @@ class Less_Parser {
'indentation' => ' ',
'plugins' => [],
];

public static $options = [];

public $cssBuffer = "";

public $preBuffer = "";

public $config;

/**
* Setup Constructor
*/
* Next file ID iterator.
* This is static so that separate instances of this class do not clash.
*
* @var integer
*/
private static $nextFileId = 0;

/**
* Setup Constructor
*/
public function __construct() {
$this->SetOptions(Less_Parser::$default_options);
$this->SetOptions(self::$default_options);
$this->Reset(null);

$config = parse_ini_file(__DIR__.'/config.defaults.ini');
if (file_exists(__DIR__."/config.ini")) {
$overrides = parse_ini_file(__DIR__.'/config.ini');
$config = parse_ini_file(__DIR__ . '/config.defaults.ini');
if (file_exists(__DIR__ . "/config.ini")) {
$overrides = parse_ini_file(__DIR__ . '/config.ini');
$config = array_replace($config, $overrides);
}
$this->config = $config;
Expand All @@ -54,24 +65,25 @@ public function getConfig($key) {

/**
* Reset Options to Default
* @param $options
*
* @param array|null $options
*/
public function Reset($options = null) {
$this->rules = [];

if (is_array($options)) {
$this->SetOptions(Less_Parser::$default_options);
$this->SetOptions(self::$default_options);
$this->SetOptions($options);
}
}

/**
* Use REST call to LESSoid to parse less.
*
* @access public
* @param string $str
* @param string $uriRoot
* @return array
* @access public
* @param string $str
* @param string $uriRoot
* @return array
*/
public function parseCallREST($str, $uriRoot = '') {
$lessoidServer = "http://localhost";
Expand All @@ -90,9 +102,9 @@ public function parseCallREST($str, $uriRoot = '') {

$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_URL, $lessoidServer.":8099/parse");
curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_URL, $lessoidServer . ":8099/parse");
curl_setopt($ch, CURLOPT_POSTFIELDS, $request);
$return = curl_exec($ch);
curl_close($ch);

Expand All @@ -107,14 +119,14 @@ public function parseCallREST($str, $uriRoot = '') {
/**
* Use the CLI to parse less.
*
* @access public
* @param string $str
* @param string $uriRoot
* @return array
* @access public
* @param string $str
* @param string $uriRoot
* @return array
*/
public function parseCLI($str, $uriRoot = '') {
$cliPath = realpath(__DIR__."/../services/lessoid/less-hydra/bin/");
$exec = $cliPath."/lessc";
$cliPath = realpath(__DIR__ . "/../services/lessoid/less-hydra/bin/");
$exec = $cliPath . "/lessc";

if (!file_exists($exec)) {
return ["message" => "Couldn't find lessc at $exec"];
Expand All @@ -126,7 +138,7 @@ public function parseCLI($str, $uriRoot = '') {
$exec .= ' --no-color';

if (isset(self::$options['import_dirs']) && is_array(self::$options['import_dirs'])) {
$paths = implode(":",array_reverse(array_keys(self::$options['import_dirs'])));
$paths = implode(":", array_reverse(array_keys(self::$options['import_dirs'])));
$exec .= " --include-path=$paths";
}

Expand All @@ -136,12 +148,12 @@ public function parseCLI($str, $uriRoot = '') {
if (is_executable($this->getConfig('node_override'))) {
$nodeBin = $this->getConfig('node_override');
} else {
return ["message" => "Your configuration for node_override (".$this->getConfig('node_override').") is not executable."];
return ["message" => "Your configuration for node_override (" . $this->getConfig('node_override') . ") is not executable."];
}
} else {
$nodeBin = 'node';
}
$exec = $nodeBin." ".$exec;
$exec = $nodeBin . " " . $exec;

$descriptorspec = [
0 => ["pipe", "r"], // stdin is a pipe that the child will read from
Expand All @@ -163,7 +175,7 @@ public function parseCLI($str, $uriRoot = '') {
/* check for errors */
$stderr = stream_get_contents($pipes[2]);
if (!empty($stderr)) {
return ["message" => $stderr];
return ["message" => $stderr];
}
fclose($pipes[2]);
proc_close($process);
Expand All @@ -179,31 +191,32 @@ public function parseCLI($str, $uriRoot = '') {
}

/**
* Parse a LESS string to CSS
* @param string $str
* @param string $fileUri
* Parse a LESS string to CSS.
*
* @param string $str
* @param string $fileUri
* @return string
*/
public function parse($str, $fileUri = '') {
// Default handling that was already hear...
// gonna just leave it?
if (!$fileUri) {
$uriRoot = '';
$filename = 'anonymous-file-'.Less_Parser::$next_id++.'.less';
$filename = 'anonymous-file-' . self::$nextFileId++ . '.less';
} else {
$fileUri = self::WinPath($fileUri);
$fileUri = $this->fixWindowsPath($fileUri);
$filename = $fileUri;
$uriRoot = dirname($fileUri);
}
$uriRoot = self::WinPath($uriRoot);
$uriRoot = $this->fixWindowsPath($uriRoot);

/* Lets ittorate and find any sub directories to check in the import path. */
if (isset(self::$options['import_dirs']) && is_array(self::$options['import_dirs'])) {
foreach (self::$options['import_dirs'] as $path => $v) {
$objects = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path), RecursiveIteratorIterator::SELF_FIRST);
foreach($objects as $name => $object) {
foreach ($objects as $name => $object) {
if ($object->isDir() && !in_array(basename($name), ['.','..'])) {
self::$options['import_dirs'][$name."/"] = "";
self::$options['import_dirs'][$name . "/"] = "";
}
}
}
Expand All @@ -214,23 +227,23 @@ public function parse($str, $fileUri = '') {
}

// Prepend string with preBuffer, for use with ModifyVars, ect.
$str = $this->preBuffer."\n\n".$str;
$str = $this->preBuffer . "\n\n" . $str;

$time_start = microtime(true);
$this->cssBuffer = "/* === Start ".basename($filename)." === */\n";
$this->cssBuffer = "/* === Start " . basename($filename) . " === */\n";

// Attempt RESTfull decoding.
$parsed = $this->parseCallREST($str, $uriRoot);

if (!isset($parsed['css'])) {
// we didn't get CSS back.
$errmsg = isset($parsed['message']) ? $parsed['message'] : "Unknown Error. ".json_encode($parsed)."";
$errmsg = isset($parsed['message']) ? $parsed['message'] : "Unknown Error. " . json_encode($parsed) . "";
$this->cssBuffer .= "\n/* No CSS Returned from REST Decoding! \n $errmsg */\n";

// Lets fallback to the CLI, the service may be down.
$parsedCli = $this->parseCLI($str, $uriRoot);
if (!isset($parsedCli['css'])) {
$errmsg = isset($parsedCli['message']) ? $parsedCli['message'] : "Unknown Error ".json_encode($parsedCli)."";
$errmsg = isset($parsedCli['message']) ? $parsedCli['message'] : "Unknown Error " . json_encode($parsedCli) . "";
$this->cssBuffer .= "\n/* No CSS Returned from CLI Decoding! \n $errmsg */\n";
} else {
// Add parsed CSS to the buffer.
Expand All @@ -242,11 +255,10 @@ public function parse($str, $fileUri = '') {
}

$time_end = microtime(true);
$this->cssBuffer .= "\n/* === End ".basename($filename)." (compiled in ".round($time_end - $time_start,2)." seconds.) === */";
$this->cssBuffer .= "\n/* === End " . basename($filename) . " (compiled in " . round($time_end - $time_start, 2) . " seconds.) === */";
return $this;
}


/**
* Parse a Less string from a given file
*
Expand All @@ -266,8 +278,8 @@ public function parseFile($filename, $uriRoot = '', $returnRoot = false) {
$uriRoot = dirname($uriRoot);
}

$filename = $filename ? self::WinPath(realpath($filename)) : false;
$uriRoot = $uriRoot ? self::WinPath($uriRoot) : '';
$filename = $filename ? $this->fixWindowsPath(realpath($filename)) : false;
$uriRoot = $uriRoot ? $this->fixWindowsPath($uriRoot) : '';

$fileContent = file_get_contents($filename);

Expand All @@ -290,70 +302,93 @@ public function getCss() {
*/
public function ModifyVars($vars) {
$s = '';
foreach($vars as $name => $value) {
$s .= (($name[0] === '@') ? '' : '@') . $name .': '. $value . ((substr($value,-1) === ';') ? '' : ';');
foreach ($vars as $name => $value) {
$value = $this->fixVar($value);
$s .= '@' . ltrim($name, '@') . ': ' . $value . ';';
}
$this->preBuffer = $s;
return $this;
}

/**
* [WinPath description]
* @param [type] $path [description]
* Fix variables to be suitable for Less.
* Example: Booleans have to be the string true or false.
*
* @access private
* @param mixed $variable
* @return mixed
*/
private function fixVar($variable) {
if (is_bool($variable)) {
$variable = $variable ? 'true' : 'false';
}
$variable = rtrim($variable, ';');
return $variable;
}

/**
* Fix paths for Windows.
*
* @access private
* @param type $path Fixed path.
*/
public static function WinPath($path) {
private function fixWindowsPath($path) {
return str_replace('\\', '/', $path);
}

/**
* Set a list of directories or callbacks the parser should use for determining import paths
*
* @param array $dirs
*/
public function SetImportDirs($dirs) {
Less_Parser::$options['import_dirs'] = array();
foreach($dirs as $path => $uriRoot) {
$path = self::WinPath($path);
self::$options['import_dirs'] = [];
foreach ($dirs as $path => $uriRoot) {
$path = $this->fixWindowsPath($path);
if (!empty($path)) {
$path = rtrim($path,'/').'/';
$path = rtrim($path, '/') . '/';
}
if (!is_callable($uriRoot)) {
$uriRoot = self::WinPath($uriRoot);
$uriRoot = $this->fixWindowsPath($uriRoot);
if (!empty($uriRoot)) {
$uriRoot = rtrim($uriRoot,'/').'/';
$uriRoot = rtrim($uriRoot, '/') . '/';
}
}
Less_Parser::$options['import_dirs'][$path] = $uriRoot;
self::$options['import_dirs'][$path] = $uriRoot;
}
}

/**
* Set Multiple Options
*
* @param array $options
*/
public function SetOptions($options) {
foreach($options as $option => $value) {
$this->SetOption($option,$value);
foreach ($options as $option => $value) {
$this->SetOption($option, $value);
}
}

/**
* Set Option
*
* @param string $option
* @param string $value
*/
public function SetOption($option,$value) {
switch($option) {
public function SetOption($option, $value) {
switch ($option) {
case 'import_dirs':
$this->SetImportDirs($value);
return;
}
Less_Parser::$options[$option] = $value;
self::$options[$option] = $value;
}

/**
* Shim to feed back empty array.
* No actual idea what mediawiki is doing with this file list, but it doesn't need it
* as far as im concerned. Tho when code expects arrays, give it arrays.
*
* @return array [an empty one!]
*/
public function AllParsedFiles() {
Expand All @@ -362,23 +397,21 @@ public function AllParsedFiles() {

/**
* Catch any calls to this class that are not implemented
* @param $name call name
* @param $arguments call arguments
*
* @param mixed $name call name
* @param mixed $arguments call arguments
* @return nothing
*/
public function __call($name, $arguments) {
return;
}

/**
* Catch any static calls to this class that are not implemented
* @param $name call name
* @param $arguments call arguments
*
* @param mixed $name call name
* @param mixed $arguments call arguments
* @return nothing
*/
public static function __callStatic($name, $arguments) {
return;
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ abstractFileManager.prototype.getPath = function (filename) {
};

abstractFileManager.prototype.tryAppendExtension = function(path, ext) {
return /(\.[a-z]*$)|([\?;].*)$/.test(path) ? path : path + ext;
if (path.lastIndexOf(ext) != path.length - ext.length) {
return path + ext;
}
return path;
};

abstractFileManager.prototype.tryAppendLessExtension = function(path) {
Expand Down
Loading

0 comments on commit 50cf89c

Please sign in to comment.