/** * @param Context $context * * @return Node|RulesetNode */ public function callCompile(Context $context) { if ($this->frames) { return $this->ruleset->compile(Context::createCopyForCompilation($context, array_merge($this->frames, $context->frames))); } return $this->ruleset->compile($context); }
/** * Visits a ruleset node. * * @param RulesetNode $node The node * @param VisitorArguments $arguments The arguments */ public function visitRuleset(RulesetNode $node, VisitorArguments $arguments) { $paths = []; if (!$node->root) { $selectors = []; foreach ($node->selectors as $selector) { /* @var $selector SelectorNode */ if ($selector->getIsOutput()) { $selectors[] = $selector; } } $node->selectors = $selectors; if (count($selectors)) { $context = $this->contexts[count($this->contexts) - 1]; $paths = $node->joinSelectors($context, $selectors); } else { $node->rules = []; } $node->paths = $paths; } $this->contexts[] = $paths; }
/** * Generates the CSS. * * @param Context $context * * @return string */ public function generateCSS(Context $context) { $output = new MappedOutput($this->contentsMap, $this); // catch the output $this->root->generateCSS($context, $output); // prepare sources foreach ($this->contentsMap as $filename => $contents) { // match md5 hash in square brackets _[#HASH#]_ // see ILess\Parser\Core::parseString() if (preg_match('/(\\[__[0-9a-f]{32}__\\])+$/', $filename)) { $filename = substr($filename, 0, -38); } $this->sources[$this->normalizeFilename($filename)] = $contents; } $sourceMapUrl = null; if ($url = $this->getOption('url')) { $sourceMapUrl = $url; } elseif ($path = $this->getOption('filename')) { $sourceMapUrl = $this->normalizeFilename($path); // naming conventions, make it foobar.css.map if (!preg_match('/\\.map$/', $sourceMapUrl)) { $sourceMapUrl = sprintf('%s.map', $sourceMapUrl); } } $sourceMapContent = $this->generateJson(); // write map to a file if ($file = $this->getOption('write_to')) { $this->saveMap($file, $sourceMapContent); } else { $sourceMap = 'data:application/json;'; if ($this->getOption('inline_encode_base64')) { $sourceMap .= 'base64,'; $sourceMapContent = base64_encode($sourceMapContent); } else { $sourceMapContent = Util::encodeURIComponent($sourceMapContent); } $sourceMapUrl = $sourceMap . $sourceMapContent; } if ($sourceMapUrl) { $output->add(sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl)); } return $output->toString(); }
/** * Compile parameters. * * @param Context $context The context * @param Context $mixinEnv The mixin environment * @param array $arguments Array of arguments * @param array $compiledArguments The compiled arguments * * @throws * * @return mixed */ public function compileParams(Context $context, Context $mixinEnv, $arguments = [], array &$compiledArguments = []) { $frame = new RulesetNode([], []); $params = $this->params; $argsCount = 0; if (isset($mixinEnv->frames[0]) && $mixinEnv->frames[0]->functionRegistry) { $frame->functionRegistry = $mixinEnv->frames[0]->functionRegistry->inherit(); } // create a copy of mixin environment $mixinEnv = Context::createCopyForCompilation($mixinEnv, array_merge([$frame], $mixinEnv->frames)); if ($arguments) { $argsCount = count($arguments); for ($i = 0; $i < $argsCount; ++$i) { if (!isset($arguments[$i])) { continue; } $arg = $arguments[$i]; if (isset($arg['name']) && ($name = $arg['name'])) { $isNamedFound = false; foreach ($params as $j => $param) { if (!isset($compiledArguments[$j]) && $name === $params[$j]['name']) { $compiledArguments[$j] = $arg['value']->compile($context); array_unshift($frame->rules, new RuleNode($name, $arg['value']->compile($context))); $isNamedFound = true; break; } } if ($isNamedFound) { array_splice($arguments, $i, 1); --$i; continue; } else { throw new CompilerException(sprintf('The named argument for `%s` %s was not found.', $this->name, $arguments[$i]['name'])); } } } } $argIndex = 0; foreach ($params as $i => $param) { if (array_key_exists($i, $compiledArguments)) { continue; } $arg = null; if (array_key_exists($argIndex, $arguments)) { $arg = $arguments[$argIndex]; } if (isset($param['name']) && ($name = $param['name'])) { if (isset($param['variadic']) && $param['variadic']) { $varArgs = []; for ($j = $argIndex; $j < $argsCount; ++$j) { $varArgs[] = $arguments[$j]['value']->compile($context); } $expression = new ExpressionNode($varArgs); array_unshift($frame->rules, new RuleNode($name, $expression->compile($context))); } else { $val = $arg && $arg['value'] ? $arg['value'] : false; if ($val) { $val = $val->compile($context); } elseif (isset($param['value'])) { $val = $param['value']->compile($mixinEnv); $frame->resetCache(); } else { throw new CompilerException(sprintf('Wrong number of arguments for `%s` (%s for %s)', $this->name, count($arguments), $this->arity)); } array_unshift($frame->rules, new RuleNode($name, $val)); $compiledArguments[$i] = $val; } } if (isset($param['variadic']) && $param['variadic'] && $arguments) { for ($j = $argIndex; $j < $argsCount; ++$j) { $compiledArguments[$j] = $arguments[$j]['value']->compile($context); } } ++$argIndex; } ksort($compiledArguments); return $frame; }
/** * Visits a ruleset node * * @param RulesetNode $node The node * @param VisitorArguments $arguments The arguments * @return array|RulesetNode */ public function visitRuleset(RulesetNode $node, VisitorArguments $arguments) { if ($node->firstRoot) { $this->checkPropertiesInRoot($node->rules); } $rulesets = []; if (!$node->root) { $paths = []; foreach ($node->paths as $p) { if ($p[0]->elements[0]->combinator->value === ' ') { $p[0]->elements[0]->combinator = new CombinatorNode(''); } foreach ($p as $pi) { /* @var $pi SelectorNode */ if ($pi->getIsReferenced() && $pi->getIsOutput()) { $paths[] = $p; break; } } } $node->paths = $paths; // Compile rules and rulesets for ($i = 0, $count = count($node->rules); $i < $count;) { $rule = $node->rules[$i]; //if ($rule instanceof ILess\ILess\Node\RuleNode || $rule instanceof ILess\ILess\Node\RulesetNode) { //if ($rule instanceof ILess\ILess\Node\RulesetNode) { if (Node::propertyExists($rule, 'rules')) { // visit because we are moving them out from being a child $rulesets[] = $this->visit($rule); array_splice($node->rules, $i, 1); $count--; continue; } $i++; } // accept the visitor to remove rules and refactor itself // then we can decide now whether we want it or not if ($count > 0) { $node->accept($this); } else { $node->rules = []; } $arguments->visitDeeper = false; // accept the visitor to remove rules and refactor itself // then we can decide now whether we want it or not if ($node->rules) { // passed by reference $this->mergeRules($node->rules); } if ($node->rules) { // passed by reference $this->removeDuplicateRules($node->rules); } // now decide whether we keep the ruleset if ($node->rules && $node->paths) { array_splice($rulesets, 0, 0, [$node]); } } else { $node->accept($this); $arguments->visitDeeper = false; if ($node->firstRoot || count($node->rules) > 0) { array_splice($rulesets, 0, 0, [$node]); } } if (count($rulesets) === 1) { return $rulesets[0]; } return $rulesets; }
/** * Compiles the node * * @param Context $context The context * @param array|null $arguments Array of arguments * @param boolean|null $important Important flag * @return array|ImportNode|MediaNode * @throws Exception * @throws LogicException */ public function compile(Context $context, $arguments = null, $important = null) { if ($this->getOption('plugin')) { $registry = isset($context->frames[0]) && $context->frames[0]->functionRegistry ? $context->frames[0]->functionRegistry : null; if ($registry && $this->root && $this->root->functions) { /* @var $registry FunctionRegistry */ foreach ($this->root->functions as $funcFile) { $registry->loadPlugin($funcFile); } } return []; } if ($this->skip) { return []; } if ($this->getOption('inline')) { $contents = new AnonymousNode($this->root, 0, new FileInfo(['filename' => $this->importedFilename, 'reference' => $this->path->currentFileInfo->reference]), true, true, false); return $this->features ? new MediaNode([$contents], $this->features->value) : [$contents]; } elseif ($this->css) { $features = $this->features ? $this->features->compile($context) : null; $import = new ImportNode($this->compilePath($context), $features, $this->options, $this->index); if (!$import->css && $this->hasError()) { throw $this->getError(); } return $import; } else { $ruleset = new RulesetNode([], $this->root ? $this->root->rules : []); $ruleset->compileImports($context); return $this->features ? new MediaNode($ruleset->rules, $this->features->value) : $ruleset->rules; } }
/** * @covers getType */ public function testGetType() { $r = new RulesetNode([new ElementNode(' ', 'foobar')], []); $this->assertEquals('Ruleset', $r->getType()); }