[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/lessphp/SourceMap/ -> Generator.php (source)

   1  <?php
   2  
   3  /**

   4   * Source map generator

   5   *

   6   * @package Less

   7   * @subpackage Output

   8   */
   9  class Less_SourceMap_Generator extends Less_Configurable {
  10  
  11      /**

  12       * What version of source map does the generator generate?

  13       */
  14      const VERSION = 3;
  15  
  16      /**

  17       * Array of default options

  18       *

  19       * @var array

  20       */
  21      protected $defaultOptions = array(
  22              // an optional source root, useful for relocating source files

  23              // on a server or removing repeated values in the 'sources' entry.

  24              // This value is prepended to the individual entries in the 'source' field.

  25              'sourceRoot'            => '',
  26  
  27              // an optional name of the generated code that this source map is associated with.

  28              'sourceMapFilename'        => null,
  29  
  30              // url of the map

  31              'sourceMapURL'            => null,
  32  
  33              // absolute path to a file to write the map to

  34              'sourceMapWriteTo'        => null,
  35  
  36              // output source contents?

  37              'outputSourceFiles'        => false,
  38  
  39              // base path for filename normalization

  40              'sourceMapRootpath'        => '',
  41  
  42              // base path for filename normalization

  43              'sourceMapBasepath'   => ''
  44      );
  45  
  46      /**

  47       * The base64 VLQ encoder

  48       *

  49       * @var Less_SourceMap_Base64VLQ

  50       */
  51      protected $encoder;
  52  
  53      /**

  54       * Array of mappings

  55       *

  56       * @var array

  57       */
  58      protected $mappings = array();
  59  
  60      /**

  61       * The root node

  62       *

  63       * @var Less_Tree_Ruleset

  64       */
  65      protected $root;
  66  
  67      /**

  68       * Array of contents map

  69       *

  70       * @var array

  71       */
  72      protected $contentsMap = array();
  73  
  74      /**

  75       * File to content map

  76       *

  77       * @var array

  78       */
  79      protected $sources = array();
  80      protected $source_keys = array();
  81  
  82      /**

  83       * Constructor

  84       *

  85       * @param Less_Tree_Ruleset $root The root node

  86       * @param array $options Array of options

  87       */
  88  	public function __construct(Less_Tree_Ruleset $root, $contentsMap, $options = array()){
  89          $this->root = $root;
  90          $this->contentsMap = $contentsMap;
  91          $this->encoder = new Less_SourceMap_Base64VLQ();
  92  
  93          $this->SetOptions($options);
  94          
  95          $this->options['sourceMapRootpath'] = $this->fixWindowsPath($this->options['sourceMapRootpath'], true);
  96          $this->options['sourceMapBasepath'] = $this->fixWindowsPath($this->options['sourceMapBasepath'], true);
  97      }
  98  
  99      /**

 100       * Generates the CSS

 101       *

 102       * @return string

 103       */
 104  	public function generateCSS(){
 105          $output = new Less_Output_Mapped($this->contentsMap, $this);
 106  
 107          // catch the output

 108          $this->root->genCSS($output);
 109  
 110  
 111          $sourceMapUrl                = $this->getOption('sourceMapURL');
 112          $sourceMapFilename            = $this->getOption('sourceMapFilename');
 113          $sourceMapContent            = $this->generateJson();
 114          $sourceMapWriteTo            = $this->getOption('sourceMapWriteTo');
 115  
 116          if( !$sourceMapUrl && $sourceMapFilename ){
 117              $sourceMapUrl = $this->normalizeFilename($sourceMapFilename);
 118          }
 119  
 120          // write map to a file

 121          if( $sourceMapWriteTo ){
 122              $this->saveMap($sourceMapWriteTo, $sourceMapContent);
 123          }
 124  
 125          // inline the map

 126          if( !$sourceMapUrl ){
 127              $sourceMapUrl = sprintf('data:application/json,%s', Less_Functions::encodeURIComponent($sourceMapContent));
 128          }
 129  
 130          if( $sourceMapUrl ){
 131              $output->add( sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl) );
 132          }
 133  
 134          return $output->toString();
 135      }
 136  
 137      /**

 138       * Saves the source map to a file

 139       *

 140       * @param string $file The absolute path to a file

 141       * @param string $content The content to write

 142       * @throws Exception If the file could not be saved

 143       */
 144  	protected function saveMap($file, $content){
 145          $dir = dirname($file);
 146          // directory does not exist

 147          if( !is_dir($dir) ){
 148              // FIXME: create the dir automatically?

 149              throw new Exception(sprintf('The directory "%s" does not exist. Cannot save the source map.', $dir));
 150          }
 151          // FIXME: proper saving, with dir write check!

 152          if(file_put_contents($file, $content) === false){
 153              throw new Exception(sprintf('Cannot save the source map to "%s"', $file));
 154          }
 155          return true;
 156      }
 157  
 158      /**

 159       * Normalizes the filename

 160       *

 161       * @param string $filename

 162       * @return string

 163       */
 164  	protected function normalizeFilename($filename){
 165  
 166          $filename = $this->fixWindowsPath($filename);
 167  
 168          $rootpath = $this->getOption('sourceMapRootpath');
 169          $basePath = $this->getOption('sourceMapBasepath');
 170  
 171          // "Trim" the 'sourceMapBasepath' from the output filename.

 172          if (strpos($filename, $basePath) === 0) {
 173              $filename = substr($filename, strlen($basePath));
 174          }
 175  
 176          // Remove extra leading path separators.

 177          if(strpos($filename, '\\') === 0 || strpos($filename, '/') === 0){
 178              $filename = substr($filename, 1);
 179          }
 180  
 181          return $rootpath . $filename;
 182      }
 183  
 184      /**

 185       * Adds a mapping

 186       *

 187       * @param integer $generatedLine The line number in generated file

 188       * @param integer $generatedColumn The column number in generated file

 189       * @param integer $originalLine The line number in original file

 190       * @param integer $originalColumn The column number in original file

 191       * @param string $sourceFile The original source file

 192       */
 193  	public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $fileInfo ){
 194  
 195          $this->mappings[] = array(
 196              'generated_line' => $generatedLine,
 197              'generated_column' => $generatedColumn,
 198              'original_line' => $originalLine,
 199              'original_column' => $originalColumn,
 200              'source_file' => $fileInfo['currentUri']
 201          );
 202  
 203          $this->sources[$fileInfo['currentUri']] = $fileInfo['filename'];
 204      }
 205  
 206  
 207      /**

 208       * Generates the JSON source map

 209       *

 210       * @return string

 211       * @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#

 212       */
 213  	protected function generateJson(){
 214  
 215          $sourceMap = array();
 216          $mappings = $this->generateMappings();
 217  
 218          // File version (always the first entry in the object) and must be a positive integer.

 219          $sourceMap['version'] = self::VERSION;
 220  
 221  
 222          // An optional name of the generated code that this source map is associated with.

 223          $file = $this->getOption('sourceMapFilename');
 224          if( $file ){
 225              $sourceMap['file'] = $file;
 226          }
 227  
 228  
 229          // An optional source root, useful for relocating source files on a server or removing repeated values in the 'sources' entry.    This value is prepended to the individual entries in the 'source' field.

 230          $root = $this->getOption('sourceRoot');
 231          if( $root ){
 232              $sourceMap['sourceRoot'] = $root;
 233          }
 234  
 235  
 236          // A list of original sources used by the 'mappings' entry.

 237          $sourceMap['sources'] = array();
 238          foreach($this->sources as $source_uri => $source_filename){
 239              $sourceMap['sources'][] = $this->normalizeFilename($source_filename);
 240          }
 241  
 242  
 243          // A list of symbol names used by the 'mappings' entry.

 244          $sourceMap['names'] = array();
 245  
 246          // A string with the encoded mapping data.

 247          $sourceMap['mappings'] = $mappings;
 248  
 249          if( $this->getOption('outputSourceFiles') ){
 250              // An optional list of source content, useful when the 'source' can't be hosted.

 251              // The contents are listed in the same order as the sources above.

 252              // 'null' may be used if some original sources should be retrieved by name.

 253              $sourceMap['sourcesContent'] = $this->getSourcesContent();
 254          }
 255  
 256          // less.js compat fixes

 257          if( count($sourceMap['sources']) && empty($sourceMap['sourceRoot']) ){
 258              unset($sourceMap['sourceRoot']);
 259          }
 260  
 261          return json_encode($sourceMap);
 262      }
 263  
 264      /**

 265       * Returns the sources contents

 266       *

 267       * @return array|null

 268       */
 269  	protected function getSourcesContent(){
 270          if(empty($this->sources)){
 271              return;
 272          }
 273          $content = array();
 274          foreach($this->sources as $sourceFile){
 275              $content[] = file_get_contents($sourceFile);
 276          }
 277          return $content;
 278      }
 279  
 280      /**

 281       * Generates the mappings string

 282       *

 283       * @return string

 284       */
 285  	public function generateMappings(){
 286  
 287          if( !count($this->mappings) ){
 288              return '';
 289          }
 290  
 291          $this->source_keys = array_flip(array_keys($this->sources));
 292  
 293  
 294          // group mappings by generated line number.

 295          $groupedMap = $groupedMapEncoded = array();
 296          foreach($this->mappings as $m){
 297              $groupedMap[$m['generated_line']][] = $m;
 298          }
 299          ksort($groupedMap);
 300  
 301          $lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine = $lastOriginalColumn = 0;
 302  
 303          foreach($groupedMap as $lineNumber => $line_map){
 304              while(++$lastGeneratedLine < $lineNumber){
 305                  $groupedMapEncoded[] = ';';
 306              }
 307  
 308              $lineMapEncoded = array();
 309              $lastGeneratedColumn = 0;
 310  
 311              foreach($line_map as $m){
 312                  $mapEncoded = $this->encoder->encode($m['generated_column'] - $lastGeneratedColumn);
 313                  $lastGeneratedColumn = $m['generated_column'];
 314  
 315                  // find the index

 316                  if( $m['source_file'] ){
 317                      $index = $this->findFileIndex($m['source_file']);
 318                      if( $index !== false ){
 319                          $mapEncoded .= $this->encoder->encode($index - $lastOriginalIndex);
 320                          $lastOriginalIndex = $index;
 321  
 322                          // lines are stored 0-based in SourceMap spec version 3

 323                          $mapEncoded .= $this->encoder->encode($m['original_line'] - 1 - $lastOriginalLine);
 324                          $lastOriginalLine = $m['original_line'] - 1;
 325  
 326                          $mapEncoded .= $this->encoder->encode($m['original_column'] - $lastOriginalColumn);
 327                          $lastOriginalColumn = $m['original_column'];
 328                      }
 329                  }
 330  
 331                  $lineMapEncoded[] = $mapEncoded;
 332              }
 333  
 334              $groupedMapEncoded[] = implode(',', $lineMapEncoded) . ';';
 335          }
 336  
 337          return rtrim(implode($groupedMapEncoded), ';');
 338      }
 339  
 340      /**

 341       * Finds the index for the filename

 342       *

 343       * @param string $filename

 344       * @return integer|false

 345       */
 346  	protected function findFileIndex($filename){
 347          return $this->source_keys[$filename];
 348      }
 349  
 350      /**

 351       * fix windows paths

 352       * @param  string $path

 353       * @return string      

 354       */
 355  	public function fixWindowsPath($path, $addEndSlash = false){
 356          $slash = ($addEndSlash) ? '/' : '';
 357          if( !empty($path) ){
 358              $path = str_replace('\\', '/', $path);
 359              $path = rtrim($path,'/') . $slash;
 360          }
 361  
 362          return $path;
 363      }
 364  
 365  }


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1