public function run() { if (version_compare(phpversion(), '5.4.0', '<')) { Console::writeWarning("you must have PHP 5.4.0 or greater to use this feature. you are using PHP " . phpversion() . "..."); } else { // set-up defaults $publicDir = Config::getOption("publicDir"); $coreDir = Config::getOption("coreDir"); $host = Console::findCommandOptionValue("host"); $host = $host ? $host : "localhost"; $port = Console::findCommandOptionValue("port"); $host = $port ? $host . ":" . $port : $host . ":8080"; $quiet = Console::findCommandOption("quiet"); // set-up the base command $command = $this->pathPHP . " -S " . $host . " " . $coreDir . "/server/router.php"; $commands = array(); $commands[] = array("command" => $command, "cwd" => $publicDir, "timeout" => null, "idle" => 1800); // get the watch command info if (Console::findCommandOption("with-watch")) { $watchCommand = new WatchCommand(); $commands[] = array("command" => $watchCommand->build() . " --no-procs", "timeout" => null, "idle" => 1800); } Console::writeInfo("server started on http://" . $host . " - use ctrl+c to exit..."); $processSpawner = new ProcessSpawner(); $processSpawner->spawn($commands, $quiet); } }
/** * Check that a dir from the config exists and try to build it if needed * @param {String} directory to be checked/built * @param {String} the config path * @param {String} the config option that would help them */ public static function checkPathFromConfig($path, $configPath, $configOption = "") { if (!isset($path)) { Console::writeError("please make sure " . $configOption . " is set in <path>" . Console::getHumanReadablePath($configPath) . "</path> by adding '" . $configOption . "=some/path'. sorry, stopping pattern lab... :("); } else { if (!is_dir($path)) { Console::writeWarning("i can't seem to find the directory <path>" . Console::getHumanReadablePath($path) . "</path>..."); self::makeDir($path); Console::writeWarning("i created <path>" . Console::getHumanReadablePath($path) . "</path> just in case. you can edit this in <path>" . Console::getHumanReadablePath($configPath) . "</path> by editing " . $configOption . "..."); } } }
public function run() { if (version_compare(phpversion(), '5.4.0', '<')) { Console::writeWarning("you must have PHP 5.4.0 or greater to use this feature. you are using PHP " . phpversion() . "..."); } else { // set-up defaults $publicDir = Config::getOption("publicDir"); $coreDir = Config::getOption("coreDir"); // start-up the server with the router Console::writeInfo("server started on localhost:8080. use ctrl+c to exit..."); passthru("cd " . $publicDir . " && " . $_SERVER["_"] . " -S localhost:8080 " . $coreDir . "/server/router.php"); } }
/** * Gather data from annotations.js and *.md files found in source/_annotations * * @return {Array} populates Annotations::$store */ public static function gather() { // set-up default var $sourceDir = Config::getOption("sourceDir"); // set-up the dispatcher $dispatcherInstance = Dispatcher::getInstance(); // dispatch that the data gather has started $dispatcherInstance->dispatch("annotations.gatherStart"); // set-up the comments store self::$store["comments"] = array(); // iterate over all of the files in the annotations dir if (!is_dir($sourceDir . "/_annotations")) { Console::writeWarning("<path>_annotations/</path><warning> doesn't exist so you won't have annotations..."); mkdir($sourceDir . "/_annotations"); } // find the markdown-based annotations $finder = new Finder(); $finder->files()->name("*.md")->in($sourceDir . "/_annotations"); $finder->sortByName(); foreach ($finder as $name => $file) { $data = array(); $data[0] = array(); $text = file_get_contents($file->getPathname()); $matches = strpos($text, PHP_EOL . "~*~" . PHP_EOL) !== false ? explode(PHP_EOL . "~*~" . PHP_EOL, $text) : array($text); foreach ($matches as $match) { list($yaml, $markdown) = Documentation::parse($match); if (isset($yaml["el"]) || isset($yaml["selector"])) { $data[0]["el"] = isset($yaml["el"]) ? $yaml["el"] : $yaml["selector"]; } else { $data[0]["el"] = "#someimpossibleselector"; } $data[0]["title"] = isset($yaml["title"]) ? $yaml["title"] : ""; $data[0]["comment"] = $markdown; self::$store["comments"] = array_merge(self::$store["comments"], $data); } } // read in the old style annotations.js, modify the data and generate JSON array to merge if (file_exists($sourceDir . "/_annotations/annotations.js")) { $text = file_get_contents($sourceDir . "/_annotations/annotations.js"); $text = str_replace("var comments = ", "", $text); $text = rtrim($text, ";"); $data = json_decode($text, true); if ($jsonErrorMessage = JSON::hasError()) { JSON::lastErrorMsg("_annotations/annotations.js", $jsonErrorMessage, $data); } } // merge in any data from the old file self::$store["comments"] = array_merge(self::$store["comments"], $data["comments"]); $dispatcherInstance->dispatch("annotations.gatherEnd"); }
public function run() { // load default vars $patternExtension = Config::getOption("patternExtension"); $patternSourceDir = Config::getOption("patternSourceDir"); // load the pattern data $store = PatternData::get(); // iterate to get raw data loaded into the PatternData Store foreach ($store as $patternStoreKey => $patternStoreData) { if ($patternStoreData["category"] == "pattern" && isset($patternStoreData["name"])) { // figure out the source path for the pattern to render $srcPath = isset($patternStoreData["pseudo"]) ? PatternData::getPatternOption($patternStoreData["original"], "pathName") : $patternStoreData["pathName"]; // load the raw data so it can be modified/rendered $path = $patternSourceDir . DIRECTORY_SEPARATOR . $srcPath . "." . $patternExtension; if (file_exists($path)) { PatternData::setPatternOption($patternStoreKey, "patternRaw", file_get_contents($path)); } else { Console::writeWarning($patternStoreData["partial"] . " wasn't found for loading. the given path: " . $path); } } } }
public function run() { // set-up default vars $foundLineages = array(); $patternSourceDir = Config::getOption("patternSourceDir"); $patternExtension = Config::getOption("patternExtension"); // check for the regular lineages in only normal patterns $store = PatternData::get(); foreach ($store as $patternStoreKey => $patternStoreData) { if ($patternStoreData["category"] == "pattern" && !isset($patternStoreData["pseudo"])) { $patternLineages = array(); $fileData = isset($patternStoreData["patternRaw"]) ? $patternStoreData["patternRaw"] : ""; $foundLineages = $this->findLineages($fileData); if (!empty($foundLineages)) { foreach ($foundLineages as $lineage) { $lineageData = PatternData::getOption($lineage); if ($lineageData) { $patternLineages[] = array("lineagePattern" => $lineage, "lineagePath" => "../../patterns/" . $lineageData["pathDash"] . "/" . $lineageData["pathDash"] . ".html"); } else { if (strpos($lineage, '/') === false) { $fileName = $patternStoreData["pathName"] . "." . $patternExtension; Console::writeWarning("you may have a typo in " . $fileName . ". {{> " . $lineage . " }} is not a valid pattern..."); } } } // add the lineages to the PatternData::$store PatternData::setPatternOption($patternStoreKey, "lineages", $patternLineages); } } } // handle all of those pseudo patterns $store = PatternData::get(); foreach ($store as $patternStoreKey => $patternStoreData) { if ($patternStoreData["category"] == "pattern" && isset($patternStoreData["pseudo"])) { // add the lineages to the PatternData::$store $patternStoreKeyOriginal = $patternStoreData["original"]; PatternData::setPatternOption($patternStoreKey, "lineages", PatternData::getPatternOption($patternStoreKeyOriginal, "lineages")); } } // check for the reverse lineages and skip pseudo patterns $store = PatternData::get(); foreach ($store as $patternStoreKey => $patternStoreData) { if ($patternStoreData["category"] == "pattern" && !isset($patternStoreData["pseudo"])) { $patternLineagesR = array(); $storeTake2 = PatternData::get(); foreach ($storeTake2 as $haystackKey => $haystackData) { if ($haystackData["category"] == "pattern" && isset($haystackData["lineages"]) && !empty($haystackData["lineages"])) { foreach ($haystackData["lineages"] as $haystackLineage) { if ($haystackLineage["lineagePattern"] == $patternStoreData["partial"]) { $foundAlready = false; foreach ($patternLineagesR as $patternCheck) { if ($patternCheck["lineagePattern"] == $patternStoreData["partial"]) { $foundAlready = true; break; } } if (!$foundAlready) { if (PatternData::getOption($haystackKey)) { $path = PatternData::getPatternOption($haystackKey, "pathDash"); $patternLineagesR[] = array("lineagePattern" => $haystackKey, "lineagePath" => "../../patterns/" . $path . "/" . $path . ".html"); } } } } } } PatternData::setPatternOption($patternStoreKey, "lineagesR", $patternLineagesR); } } // handle all of those pseudo patterns $store = PatternData::get(); foreach ($store as $patternStoreKey => $patternStoreData) { if ($patternStoreData["category"] == "pattern" && isset($patternStoreData["pseudo"])) { // add the lineages to the PatternData::$store $patternStoreKeyOriginal = $patternStoreData["original"]; PatternData::setPatternOption($patternStoreKey, "lineagesR", PatternData::getPatternOption($patternStoreKeyOriginal, "lineagesR")); } } }
/** * Update a single config option based on a change in composer.json * @param {String} the name of the option to be changed * @param {String} the new value of the option to be changed */ public static function updateConfigOption($optionName, $optionValue) { if (is_string($optionValue) && strpos($optionValue, "<prompt>") !== false) { // prompt for input using the supplied query $prompt = str_replace("</prompt>", "", str_replace("<prompt>", "", $optionValue)); $options = ""; $input = Console::promptInput($prompt, $options, false); self::writeUpdateConfigOption($optionName, $input); Console::writeTag("ok", "config option " . $optionName . " updated...", false, true); } else { if (!isset(self::$options[$optionName]) || self::$options["overrideConfig"] == "a") { // if the option isn't set or the config is always to override update the config self::writeUpdateConfigOption($optionName, $optionValue); } else { if (self::$options["overrideConfig"] == "q") { // standardize the values for comparison $currentOptionValue = is_array(self::$options[$optionName]) ? implode(", ", self::$options[$optionName]) : self::$options[$optionName]; $newOptionValue = is_array($optionValue) ? implode(", ", $optionValue) : $optionValue; if ($currentOptionValue != $newOptionValue) { // prompt for input $prompt = "update the config option <desc>" . $optionName . " (" . $currentOptionValue . ")</desc> with the value <desc>" . $newOptionValue . "</desc>?"; $options = "Y/n"; $input = Console::promptInput($prompt, $options); if ($input == "y") { self::writeUpdateConfigOption($optionName, $optionValue); Console::writeInfo("config option " . $optionName . " updated...", false, true); } else { Console::writeWarning("config option <desc>" . $optionName . "</desc> not updated...", false, true); } } } } } }
/** * Gather data from any JSON and YAML files in source/_data * * Reserved attributes: * - Data::$store["listItems"] : listItems from listitems.json, duplicated into separate arrays for Data::$store["listItems"]["one"], Data::$store["listItems"]["two"]... etc. * - Data::$store["link"] : the links to each pattern * - Data::$store["cacheBuster"] : the cache buster value to be appended to URLs * - Data::$store["patternSpecific"] : holds attributes from the pattern-specific data files * * @return {Array} populates Data::$store */ public static function gather($options = array()) { // set-up the dispatcher $dispatcherInstance = Dispatcher::getInstance(); // dispatch that the data gather has started $dispatcherInstance->dispatch("data.gatherStart"); // default vars $found = false; $dataJSON = array(); $dataYAML = array(); $listItemsJSON = array(); $listItemsYAML = array(); $sourceDir = Config::getOption("sourceDir"); // iterate over all of the other files in the source directory if (!is_dir($sourceDir . "/_data/")) { Console::writeWarning("<path>_data/</path> doesn't exist so you won't have dynamic data..."); mkdir($sourceDir . "/_data/"); } // find the markdown-based annotations $finder = new Finder(); $finder->files()->in($sourceDir . "/_data/"); $finder->sortByName(); foreach ($finder as $name => $file) { $ext = $file->getExtension(); $data = array(); $fileName = $file->getFilename(); $hidden = $fileName[0] == "_"; $isListItems = strpos($fileName, "listitems"); $pathName = $file->getPathname(); $pathNameClean = str_replace($sourceDir . "/", "", $pathName); if (!$hidden && ($ext == "json" || $ext == "yaml")) { if ($isListItems === false) { if ($ext == "json") { $file = file_get_contents($pathName); $data = json_decode($file, true); if ($jsonErrorMessage = JSON::hasError()) { JSON::lastErrorMsg($pathNameClean, $jsonErrorMessage, $data); } } else { if ($ext == "yaml") { $file = file_get_contents($pathName); try { $data = YAML::parse($file); } catch (ParseException $e) { printf("unable to parse " . $pathNameClean . ": %s..\n", $e->getMessage()); } // single line of text won't throw a YAML error. returns as string if (gettype($data) == "string") { $data = array(); } } } if (is_array($data)) { self::$store = array_replace_recursive(self::$store, $data); } } else { if ($isListItems !== false) { $data = $ext == "json" ? self::getListItems("_data/" . $fileName) : self::getListItems("_data/" . $fileName, "yaml"); if (!isset(self::$store["listItems"])) { self::$store["listItems"] = array(); } self::$store["listItems"] = array_replace_recursive(self::$store["listItems"], $data); } } } } if (is_array(self::$store)) { foreach (self::$reservedKeys as $reservedKey) { if (array_key_exists($reservedKey, self::$store)) { Console::writeWarning("\"" . $reservedKey . "\" is a reserved key in Pattern Lab. the data using that key will be overwritten. please choose a new key..."); } } } self::$store["cacheBuster"] = Config::getOption("cacheBuster"); self::$store["link"] = array(); self::$store["patternSpecific"] = array(); $dispatcherInstance->dispatch("data.gatherEnd"); }
protected function starterKitPathPrompt() { // need to figure this out long-term InstallerUtil::$isInteractive = true; $input = Console::promptInput("Tell me the path to the starterkit you want to watch.", "e.g. vendor/pattern-lab/starterkit-mustache-demo/dist", "baz", false); // set-up the full starterkit path $starterKitPath = Config::getOption("baseDir") . $input; if (!is_dir($starterKitPath)) { Console::writeWarning("that doesn't seem to be a real directory. let's try again..."); $starterKitPath = $this->starterKitPathPrompt(); } return $starterKitPath; }
/** * Write a warning if a dir doesn't exist * @param {String} the dir that doesn't exist */ protected static function dirNotExist($dir) { $dirHR = Console::getHumanReadablePath($dir); Console::writeWarning("the path <path>" . $dirHR . "</path> doesn't exist so filters won't be loaded..."); }
/** * Ask questions after the create package is done * @param {Object} a script event object from composer */ public static function postCreateProjectCmd($event) { // see if there is an extra component $extra = $event->getComposer()->getPackage()->getExtra(); if (isset($extra["patternlab"])) { self::init(); Console::writeLine(""); // see if we have any starterkits to suggest if (isset($extra["patternlab"]["starterKitSuggestions"]) && is_array($extra["patternlab"]["starterKitSuggestions"])) { $suggestions = $extra["patternlab"]["starterKitSuggestions"]; // suggest starterkits Console::writeInfo("suggested starterkits that work with this edition:", false, true); foreach ($suggestions as $i => $suggestion) { // write each suggestion $num = $i + 1; Console::writeLine($num . ": " . $suggestion, true); } // prompt for input on the suggestions Console::writeLine(""); $prompt = "choose an option or hit return to skip:"; $options = "(ex. 1)"; $input = Console::promptInput($prompt, $options); $result = (int) $input - 1; if (isset($suggestions[$result])) { Console::writeLine(""); $f = new Fetch(); $result = $f->fetchStarterKit($suggestions[$result]); if ($result) { Console::writeLine(""); $g = new Generator(); $g->generate(array("foo" => "bar")); Console::writeLine(""); Console::writeInfo("type <desc>php core/console --server</desc> to start the built-in server and see Pattern Lab...", false, true); } } else { Console::writeWarning("you will need to install a StarterKit before using Pattern Lab..."); } } } }
/** * Check to see if the path already exists. If it does prompt the user to double-check it should be overwritten * @param {String} the package name * @param {String} path to be checked * * @return {Boolean} if the path exists and should be overwritten */ protected static function pathExists($packageName, $path) { $fs = new Filesystem(); if ($fs->exists($path)) { // set-up a human readable prompt $humanReadablePath = str_replace(Config::getOption("baseDir"), "./", $path); // set if the prompt should fire $prompt = true; // are we checking a directory? if (is_dir($path)) { // see if the directory is essentially empty $files = scandir($path); foreach ($files as $key => $file) { $ignore = array("..", ".", ".gitkeep", "README", ".DS_Store", "patternlab-components"); $file = explode("/", $file); if (in_array($file[count($file) - 1], $ignore)) { unset($files[$key]); } } if (empty($files)) { $prompt = false; } } if ($prompt) { // prompt for input using the supplied query $prompt = "the path <path>" . $humanReadablePath . "</path> already exists. merge or replace with the contents of <path>" . $packageName . "</path> package?"; $options = "M/r"; $input = Console::promptInput($prompt, $options, "M"); if ($input == "m") { Console::writeTag("ok", "contents of <path>" . $humanReadablePath . "</path> have been merged with the package's content...", false, true); return false; } else { Console::writeWarning("contents of <path>" . $humanReadablePath . "</path> have been replaced by the package's content...", false, true); return true; } } return false; } return false; }
protected function starterKitSuggestions() { Console::writeLine(""); $composerPath = Config::getOption("baseDir") . "/composer.json"; if (file_exists($composerPath)) { $json = file_get_contents($composerPath); $data = json_decode($json, true); if ($jsonErrorMessage = JSON::hasError()) { JSON::lastErrorMsg(Console::getHumanReadablePath($oldStyleAnnotationsPath), $jsonErrorMessage, $data); } if (isset($data["extra"]) && isset($data["extra"]["patternlab"]) && isset($data["extra"]["patternlab"]["starterKitSuggestions"])) { $starterKitSuggestions = $data["extra"]["patternlab"]["starterKitSuggestions"]; Console::writeInfo("suggested starterkits that work with this edition:", false, true); foreach ($starterKitSuggestions as $i => $suggestion) { $num = $i + 1; Console::writeLine($num . ": " . $suggestion, true); } // hack around installer util feature in Console::promptInput InstallerUtil::$isInteractive = true; // prompt for input on the suggestions Console::writeLine(""); $prompt = "choose an option or hit return to cancel:"; $options = "(ex. 1)"; $input = Console::promptInput($prompt, $options, "1"); $result = (int) $input - 1; if (isset($starterKitSuggestions[$result])) { Console::writeLine(""); $f = new Fetch(); $result = $f->fetchStarterKit($starterKitSuggestions[$result]); } } else { Console::writeWarning("this edition has no starterkits to suggested...", false, true); } } else { Console::writeError("can't find composer.json to get suggestions...", false, true); } }
/** * Update a single config option based on a change in composer.json * @param {String} the name of the option to be changed * @param {String} the new value of the option to be changed * @param {Boolean} whether to force the update of the option */ public static function updateConfigOption($optionName, $optionValue, $force = false) { if (is_string($optionValue) && strpos($optionValue, "<prompt>") !== false) { // prompt for input using the supplied query $options = ""; $default = ""; $prompt = str_replace("</prompt>", "", str_replace("<prompt>", "", $optionValue)); if (strpos($prompt, "<default>") !== false) { $default = explode("<default>", $prompt); $default = explode("</default>", $default[1]); $default = $default[0]; } $input = Console::promptInput($prompt, $options, $default, false); self::writeUpdateConfigOption($optionName, $input); Console::writeTag("ok", "config option " . $optionName . " updated...", false, true); } else { if (!isset(self::$options[$optionName]) || self::$options["overrideConfig"] == "a" || $force) { // if the option isn't set or the config is always to override update the config self::writeUpdateConfigOption($optionName, $optionValue); } else { if (self::$options["overrideConfig"] == "q") { // standardize the values for comparison $currentOption = self::getOption($optionName); $currentOptionValue = $currentOption; $newOptionValue = $optionValue; $optionNameOutput = $optionName; // dive into plugins to do a proper comparison to if ($optionName == "plugins") { // replace the data in anticipation of it being used $optionValue = array_replace_recursive($currentOption, $newOptionValue); // get the key of the plugin that is being added/updated reset($newOptionValue); $newOptionKey = key($newOptionValue); if (!array_key_exists($newOptionKey, $currentOptionValue)) { // if the key doesn't exist just write out the new config and move on self::writeUpdateConfigOption($optionName, $optionValue); return; } else { // see if the existing configs for the plugin exists. if so just return with no changes if ($newOptionValue[$newOptionKey] == $currentOptionValue[$newOptionKey]) { return; } else { $optionNameOutput = $optionName . "." . $newOptionKey; } } } if ($currentOptionValue != $newOptionValue) { // prompt for input if (is_array($currentOptionValue)) { $prompt = "update the config option <desc>" . $optionNameOutput . "</desc> with the value from the package install?"; } else { $prompt = "update the config option <desc>" . $optionNameOutput . " (" . $currentOptionValue . ")</desc> with the value <desc>" . $newOptionValue . "</desc>?"; } $options = "Y/n"; $input = Console::promptInput($prompt, $options, "Y"); if ($input == "y") { // update the config option self::writeUpdateConfigOption($optionName, $optionValue); Console::writeInfo("config option " . $optionNameOutput . " updated...", false, true); } else { Console::writeWarning("config option <desc>" . $optionNameOutput . "</desc> not updated...", false, true); } } } } } }