/** * 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 $annotationsDir = Config::getOption("annotationsDir"); // 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(); // create the annotations dir if it doesn't exist if (!is_dir($annotationsDir)) { mkdir($annotationsDir); } // find the markdown-based annotations $finder = new Finder(); $finder->files()->name("*.md")->in($annotationsDir); $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 $data = array(); $oldStyleAnnotationsPath = $annotationsDir . DIRECTORY_SEPARATOR . "annotations.js"; if (file_exists($oldStyleAnnotationsPath)) { $text = trim(file_get_contents($oldStyleAnnotationsPath)); $text = str_replace("var comments = ", "", $text); if ($text[strlen($text) - 1] == ";") { $text = rtrim($text, ";"); } $data = json_decode($text, true); if ($jsonErrorMessage = JSON::hasError()) { JSON::lastErrorMsg(Console::getHumanReadablePath($oldStyleAnnotationsPath), $jsonErrorMessage, $data); } } // merge in any data from the old file if the json decode was successful if (is_array($data) && isset($data["comments"])) { self::$store["comments"] = array_merge(self::$store["comments"], $data["comments"]); } $dispatcherInstance->dispatch("annotations.gatherEnd"); }
/** * 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 . "..."); } } }
/** * Set-up default vars */ public static function init() { // make sure config vars exist if (!Config::getOption("patternExtension")) { Console::writeError("the pattern extension config option needs to be set..."); } if (!Config::getOption("styleguideKit")) { Console::writeError("the styleguideKit config option needs to be set..."); } // set-up config vars $patternExtension = Config::getOption("patternExtension"); $metaDir = Config::getOption("metaDir"); $styleguideKit = Config::getOption("styleguideKit"); $styleguideKitPath = Config::getOption("styleguideKitPath"); if (!$styleguideKitPath || !is_dir($styleguideKitPath)) { Console::writeError("your styleguide won't render because i can't find your styleguide files. are you sure they're at <path>" . Console::getHumanReadablePath($styleguideKitPath) . "</path>? you can fix this in <path>./config/config.yml</path> by editing styleguideKitPath..."); } // load pattern-lab's resources $partialPath = $styleguideKitPath . DIRECTORY_SEPARATOR . "views" . DIRECTORY_SEPARATOR . "partials"; $generalHeaderPath = $partialPath . DIRECTORY_SEPARATOR . "general-header." . $patternExtension; $generalFooterPath = $partialPath . DIRECTORY_SEPARATOR . "general-footer." . $patternExtension; self::$htmlHead = file_exists($generalHeaderPath) ? file_get_contents($generalHeaderPath) : ""; self::$htmlFoot = file_exists($generalFooterPath) ? file_get_contents($generalFooterPath) : ""; // gather the user-defined header and footer information $patternHeadPath = $metaDir . DIRECTORY_SEPARATOR . "_00-head." . $patternExtension; $patternFootPath = $metaDir . DIRECTORY_SEPARATOR . "_01-foot." . $patternExtension; self::$patternHead = file_exists($patternHeadPath) ? file_get_contents($patternHeadPath) : ""; self::$patternFoot = file_exists($patternFootPath) ? file_get_contents($patternFootPath) : ""; // add the filesystemLoader $patternEngineBasePath = PatternEngine::getInstance()->getBasePath(); $filesystemLoaderClass = $patternEngineBasePath . "\\Loaders\\FilesystemLoader"; $options = array(); $options["templatePath"] = $styleguideKitPath . DIRECTORY_SEPARATOR . "views"; $options["partialsPath"] = $options["templatePath"] . DIRECTORY_SEPARATOR . "partials"; self::$filesystemLoader = new $filesystemLoaderClass($options); $stringLoaderClass = $patternEngineBasePath . "\\Loaders\\StringLoader"; self::$stringLoader = new $stringLoaderClass(); // i can't remember why i chose to implement the pattern loader directly in classes // i figure i had a good reason which is why it's not showing up here }
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); } }
/** * 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..."); }
/** * Generates the data that powers the index page */ protected function generateIndex() { // bomb if missing index.html if (!file_exists(Config::getOption("publicDir") . "/index.html")) { $index = Console::getHumanReadablePath(Config::getOption("publicDir")) . DIRECTORY_SEPARATOR . "index.html"; Console::writeError("<path>" . $index . "</path> is missing. grab a copy from your StyleguideKit..."); } // set-up the dispatcher $dispatcherInstance = Dispatcher::getInstance(); // note the start of the operation $dispatcherInstance->dispatch("builder.generateIndexStart"); // default var $dataDir = Config::getOption("publicDir") . "/styleguide/data"; // double-check that the data directory exists if (!is_dir($dataDir)) { FileUtil::makeDir($dataDir); } $output = ""; // load and write out the config options $config = array(); $exposedOptions = Config::getOption("exposedOptions"); foreach ($exposedOptions as $exposedOption) { $config[$exposedOption] = Config::getOption($exposedOption); } $output .= "var config = " . json_encode($config) . ";\n"; // load the ish Controls $ishControls = array(); $controlsToHide = array(); $ishControlsHide = Config::getOption("ishControlsHide"); if ($ishControlsHide) { foreach ($ishControlsHide as $controlToHide) { $controlsToHide[$controlToHide] = "true"; } } $ishControls["ishControlsHide"] = $controlsToHide; $output .= "var ishControls = " . json_encode($ishControls) . ";\n"; // load and write out the items for the navigation $niExporter = new NavItemsExporter(); $navItems = $niExporter->run(); $output .= "var navItems = " . json_encode($navItems) . ";\n"; // load and write out the items for the pattern paths $patternPaths = array(); $ppdExporter = new PatternPathDestsExporter(); $patternPaths = $ppdExporter->run(); $output .= "var patternPaths = " . json_encode($patternPaths) . ";\n"; // load and write out the items for the view all paths $viewAllPaths = array(); $vapExporter = new ViewAllPathsExporter(); $viewAllPaths = $vapExporter->run($navItems); $output .= "var viewAllPaths = " . json_encode($viewAllPaths) . ";\n"; // gather plugin package information $packagesInfo = array(); $componentDir = Config::getOption("componentDir"); if (!is_dir($componentDir)) { mkdir($componentDir); } $componentPackagesDir = $componentDir . "/packages"; if (!is_dir($componentDir . "/packages")) { mkdir($componentDir . "/packages"); } $finder = new Finder(); $finder->files()->name("*.json")->in($componentPackagesDir); $finder->sortByName(); foreach ($finder as $file) { $filename = $file->getFilename(); if ($filename[0] != "_") { $javascriptPaths = array(); $packageInfo = json_decode(file_get_contents($file->getPathname()), true); foreach ($packageInfo["templates"] as $templateKey => $templatePath) { $templatePathFull = $componentDir . "/" . $packageInfo["name"] . "/" . $templatePath; $packageInfo["templates"][$templateKey] = file_exists($templatePathFull) ? file_get_contents($templatePathFull) : ""; } foreach ($packageInfo["javascripts"] as $key => $javascriptPath) { $javascriptPaths[] = "patternlab-components/" . $packageInfo["name"] . "/" . $javascriptPath; } $packageInfo["javascripts"] = $javascriptPaths; $packagesInfo[] = $packageInfo; } } $output .= "var plugins = " . json_encode($packagesInfo) . ";"; // write out the data file_put_contents($dataDir . "/patternlab-data.js", $output); // note the end of the operation $dispatcherInstance->dispatch("builder.generateIndexEnd"); }
/** * Gather all of the information related to the patterns */ public static function gather($options = array()) { // set default vars $exportClean = isset($options["exportClean"]) ? $options["exportClean"] : false; $exportFiles = isset($options["exportClean"]) ? $options["exportFiles"] : false; $dispatcherInstance = Dispatcher::getInstance(); // cleaning the var for use below, i know this is stupid $options = array(); // dispatch that the data gather has started $event = new PatternDataEvent($options); $dispatcherInstance->dispatch("patternData.gatherStart", $event); // load up the rules for parsing patterns and the directories self::loadRules($options); // dispatch that the rules are loaded $event = new PatternDataEvent($options); $dispatcherInstance->dispatch("patternData.rulesLoaded", $event); // iterate over the patterns & related data and regenerate the entire site if they've changed // seems a little silly to use symfony finder here. not really giving me any power if (!is_dir(Config::getOption("patternSourceDir"))) { Console::writeError("having patterns is important. please make sure you've installed a starterkit and/or that " . Console::getHumanReadablePath(Config::getOption("patternSourceDir")) . " exists..."); } $patternObjects = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator(Config::getOption("patternSourceDir")), \RecursiveIteratorIterator::SELF_FIRST); $patternObjects->setFlags(\FilesystemIterator::SKIP_DOTS); // sort the returned objects $patternObjects = iterator_to_array($patternObjects); ksort($patternObjects); $patternSourceDir = Config::getOption("patternSourceDir"); foreach ($patternObjects as $name => $object) { $ext = $object->getExtension(); $isDir = $object->isDir(); $isFile = $object->isFile(); $path = str_replace($patternSourceDir . DIRECTORY_SEPARATOR, "", $object->getPath()); $pathName = str_replace($patternSourceDir . DIRECTORY_SEPARATOR, "", $object->getPathname()); $name = $object->getFilename(); $depth = substr_count($pathName, DIRECTORY_SEPARATOR); // iterate over the rules and see if the current file matches one, if so run the rule foreach (self::$rules as $rule) { if ($rule->test($depth, $ext, $isDir, $isFile, $name)) { $rule->run($depth, $ext, $path, $pathName, $name); } } } // dispatch that the data is loaded $event = new PatternDataEvent($options); $dispatcherInstance->dispatch("patternData.dataLoaded", $event); // make sure all of the appropriate pattern data is pumped into $this->d for rendering patterns $dataLinkExporter = new DataLinkExporter(); $dataLinkExporter->run(); // make sure all of the appropriate pattern data is pumped into $this->d for rendering patterns $dataMergeExporter = new DataMergeExporter(); $dataMergeExporter->run(); // dispatch that the raw pattern helper is about to start $event = new PatternDataEvent($options); $dispatcherInstance->dispatch("patternData.rawPatternHelperStart", $event); // add the lineage info to PatternData::$store $rawPatternHelper = new RawPatternHelper(); $rawPatternHelper->run(); // dispatch that the raw pattern helper is ended $event = new PatternDataEvent($options); $dispatcherInstance->dispatch("patternData.rawPatternHelperEnd", $event); // dispatch that the lineage helper is about to start $event = new PatternDataEvent($options); $dispatcherInstance->dispatch("patternData.lineageHelperStart", $event); // add the lineage info to PatternData::$store $lineageHelper = new LineageHelper(); $lineageHelper->run(); // dispatch that the lineage helper is ended $event = new PatternDataEvent($options); $dispatcherInstance->dispatch("patternData.lineageHelperEnd", $event); // dispatch that the pattern state helper is about to start $event = new PatternDataEvent($options); $dispatcherInstance->dispatch("patternData.patternStateHelperStart", $event); // using the lineage info update the pattern states on PatternData::$store $patternStateHelper = new PatternStateHelper(); $patternStateHelper->run(); // dispatch that the pattern state helper is ended $event = new PatternDataEvent($options); $dispatcherInstance->dispatch("patternData.patternStateHelperEnd", $event); // set-up code pattern paths $ppdExporter = new PatternPathSrcExporter(); $patternPathSrc = $ppdExporter->run(); $options = array(); $options["patternPaths"] = $patternPathSrc; // dispatch that the code helper is about to start $event = new PatternDataEvent($options); $dispatcherInstance->dispatch("patternData.codeHelperStart", $event); // render out all of the patterns and store the generated info in PatternData::$store $options["exportFiles"] = $exportFiles; $options["exportClean"] = $exportClean; $patternCodeHelper = new PatternCodeHelper($options); $patternCodeHelper->run(); // dispatch that the pattern code helper is ended $event = new PatternDataEvent($options); $dispatcherInstance->dispatch("patternData.patternCodeHelperEnd", $event); // dispatch that the gather has ended $event = new PatternDataEvent($options); $dispatcherInstance->dispatch("patternData.gatherEnd", $event); }
/** * Get the path to the calling script. Should be core/console but let's not make that assumption * * @return {String} the path to the calling script */ public static function getPathConsole() { $console = isset($_SERVER["SCRIPT_NAME"]) ? $_SERVER["SCRIPT_NAME"] : Config::getOption("phpScriptName"); if (!$console) { $configPath = Console::getHumanReadablePath(Config::getOption("configPath")); Console::writeError("please add the option `phpScriptName` with the path to your console option (e.g. core" . DIRECTORY_SEPARATOR . "console) to <path>" . $configPath . "</path> to run this process..."); } return Config::getOption("baseDir") . $console; }
/** * Adds the config options to a var to be accessed from the rest of the system * If it's an old config or no config exists this will update and generate it. * @param {Boolean} whether we should print out the status of the config being loaded */ public static function init($baseDir = "", $verbose = true) { // make sure a base dir was supplied if (empty($baseDir)) { Console::writeError("need a base directory to initialize the config class..."); } // normalize the baseDir $baseDir = FileUtil::normalizePath($baseDir); // double-check the default config file exists if (!is_dir($baseDir)) { Console::writeError("make sure " . $baseDir . " exists..."); } // set the baseDir option self::$options["baseDir"] = $baseDir[strlen($baseDir) - 1] == DIRECTORY_SEPARATOR ? $baseDir : $baseDir . DIRECTORY_SEPARATOR; // set-up the paths self::$userConfigDirClean = self::$options["baseDir"] . self::$userConfigDirClean; self::$userConfigDirDash = self::$options["baseDir"] . self::$userConfigDirDash; self::$userConfigDir = is_dir(self::$userConfigDirDash) ? self::$userConfigDirDash : self::$userConfigDirClean; self::$userConfigPath = self::$userConfigDir . DIRECTORY_SEPARATOR . self::$userConfig; self::$plConfigPath = self::$options["baseDir"] . "vendor/pattern-lab/core/" . self::$plConfigPath; // can't add __DIR__ above so adding here if (!is_dir(self::$userConfigDir)) { mkdir(self::$userConfigDir); } // check to see if the user config exists, if not create it if ($verbose) { Console::writeLine("configuring pattern lab..."); } // make sure migrate doesn't happen by default $migrate = false; $diffVersion = false; $defaultOptions = array(); $userOptions = array(); // double-check the default config file exists if (!file_exists(self::$plConfigPath)) { Console::writeError("the default options for Pattern Lab don't seem to exist at <path>" . Console::getHumanReadablePath(self::$plConfigPath) . "</path>. please check on the install location of pattern lab..."); } // set the default config using the pattern lab config try { $defaultOptions = Yaml::parse(file_get_contents(self::$plConfigPath)); self::$options = array_merge(self::$options, $defaultOptions); } catch (ParseException $e) { Console::writeError("Config parse error in <path>" . Console::getHumanReadablePath(self::$plConfigPath) . "</path>: " . $e->getMessage()); } // double-check the user's config exists. if not mark that we should migrate the default one if (file_exists(self::$userConfigPath)) { try { $userOptions = Yaml::parse(file_get_contents(self::$userConfigPath)); self::$options = array_merge(self::$options, $userOptions); } catch (ParseException $e) { Console::writeError("Config parse error in <path>" . Console::getHumanReadablePath(self::$userConfigPath) . "</path>: " . $e->getMessage()); } } else { $migrate = true; } // compare version numbers $diffVersion = isset($userOptions["v"]) && $userOptions["v"] == $defaultOptions["v"] ? false : true; // run an upgrade and migrations if necessary if ($migrate || $diffVersion) { if ($verbose) { Console::writeInfo("upgrading your version of pattern lab..."); } if ($migrate) { if (!@copy(self::$plConfigPath, self::$userConfigPath)) { Console::writeError("make sure that Pattern Lab can write a new config to " . self::$userConfigPath . "..."); exit; } } else { self::$options = self::writeNewConfigFile(self::$options, $defaultOptions); } } // making sure the config isn't empty if (empty(self::$options) && $verbose) { Console::writeError("a set of configuration options is required to use Pattern Lab..."); exit; } // set-up the various dirs self::$options["configDir"] = self::$userConfigDir; self::$options["configPath"] = self::$userConfigPath; self::$options["coreDir"] = is_dir(self::$options["baseDir"] . "_core") ? self::$options["baseDir"] . "_core" : self::$options["baseDir"] . "core"; self::$options["exportDir"] = isset(self::$options["exportDir"]) ? self::$options["baseDir"] . self::cleanDir(self::$options["exportDir"]) : self::$options["baseDir"] . "exports"; self::$options["publicDir"] = isset(self::$options["publicDir"]) ? self::$options["baseDir"] . self::cleanDir(self::$options["publicDir"]) : self::$options["baseDir"] . "public"; self::$options["scriptsDir"] = isset(self::$options["scriptsDir"]) ? self::$options["baseDir"] . self::cleanDir(self::$options["scriptsDir"]) : self::$options["baseDir"] . "scripts"; self::$options["sourceDir"] = isset(self::$options["sourceDir"]) ? self::$options["baseDir"] . self::cleanDir(self::$options["sourceDir"]) : self::$options["baseDir"] . "source"; self::$options["componentDir"] = isset(self::$options["componentDir"]) ? self::$options["publicDir"] . DIRECTORY_SEPARATOR . self::cleanDir(self::$options["componentDir"]) : self::$options["publicDir"] . DIRECTORY_SEPARATOR . "patternlab-components"; self::$options["dataDir"] = isset(self::$options["dataDir"]) ? self::$options["sourceDir"] . DIRECTORY_SEPARATOR . self::cleanDir(self::$options["dataDir"]) : self::$options["sourceDir"] . DIRECTORY_SEPARATOR . "_data"; self::$options["patternExportDir"] = isset(self::$options["patternExportDir"]) ? self::$options["exportDir"] . DIRECTORY_SEPARATOR . self::cleanDir(self::$options["patternExportDir"]) : self::$options["exportDir"] . DIRECTORY_SEPARATOR . "patterns"; self::$options["patternPublicDir"] = isset(self::$options["patternPublicDir"]) ? self::$options["publicDir"] . DIRECTORY_SEPARATOR . self::cleanDir(self::$options["patternPublicDir"]) : self::$options["publicDir"] . DIRECTORY_SEPARATOR . "patterns"; self::$options["patternSourceDir"] = isset(self::$options["patternSourceDir"]) ? self::$options["sourceDir"] . DIRECTORY_SEPARATOR . self::cleanDir(self::$options["patternSourceDir"]) : self::$options["sourceDir"] . DIRECTORY_SEPARATOR . "_patterns"; self::$options["metaDir"] = isset(self::$options["metaDir"]) ? self::$options["sourceDir"] . DIRECTORY_SEPARATOR . self::cleanDir(self::$options["metaDir"]) : self::$options["sourceDir"] . DIRECTORY_SEPARATOR . "_meta/"; self::$options["annotationsDir"] = isset(self::$options["annotationsDir"]) ? self::$options["sourceDir"] . DIRECTORY_SEPARATOR . self::cleanDir(self::$options["annotationsDir"]) : self::$options["sourceDir"] . DIRECTORY_SEPARATOR . "_annotations/"; // set-up outputFileSuffixes self::$options["outputFileSuffixes"]["rendered"] = isset(self::$options["outputFileSuffixes"]["rendered"]) ? self::$options["outputFileSuffixes"]["rendered"] : ''; self::$options["outputFileSuffixes"]["rawTemplate"] = isset(self::$options["outputFileSuffixes"]["rawTemplate"]) ? self::$options["outputFileSuffixes"]["rawTemplate"] : ''; self::$options["outputFileSuffixes"]["markupOnly"] = isset(self::$options["outputFileSuffixes"]["markupOnly"]) ? self::$options["outputFileSuffixes"]["markupOnly"] : '.markup-only'; // handle a pre-2.1.0 styleguideKitPath before saving it if (isset(self::$options["styleguideKitPath"])) { self::$options["styleguideKitPath"] = self::$options["baseDir"] . self::cleanDir(self::getStyleguideKitPath(self::$options["styleguideKitPath"])); } // double-check a few directories are real and set-up FileUtil::checkPathFromConfig(self::$options["sourceDir"], self::$userConfigPath, "sourceDir"); FileUtil::checkPathFromConfig(self::$options["publicDir"], self::$userConfigPath, "publicDir"); // make sure styleguideExcludes is set to an array even if it's empty if (is_string(self::$options["styleGuideExcludes"])) { self::$options["styleGuideExcludes"] = array(); } // set the cacheBuster self::$options["cacheBuster"] = self::$options["cacheBusterOn"] == "false" ? 0 : time(); // provide the default for enable CSS. performance hog so it should be run infrequently self::$options["enableCSS"] = false; // which of these should be exposed in the front-end? self::$options["exposedOptions"] = array(); self::setExposedOption("cacheBuster"); self::setExposedOption("defaultPattern"); self::setExposedOption("defaultShowPatternInfo"); self::setExposedOption("ishFontSize"); self::setExposedOption("ishMaximum"); self::setExposedOption("ishMinimum"); self::setExposedOption("outputFileSuffixes"); self::setExposedOption("plugins"); }