From 4684daf00f350d33813549f60c5ffb5913a0b8f8 Mon Sep 17 00:00:00 2001 From: opsb Date: Sat, 15 Jul 2017 11:09:01 +0200 Subject: [PATCH] Add CLI - generates css and writes to a file --- .gitignore | 4 ++- bin/style-elements.js | 47 ++++++++++++++++++++++++++ index.js | 76 +++++++++++++++++++++++++++++++++++++++++++ js/utils.js | 48 +++++++++++++++++++++++++++ package.json | 32 ++++++++++++++++++ 5 files changed, 206 insertions(+), 1 deletion(-) create mode 100755 bin/style-elements.js create mode 100644 index.js create mode 100644 js/utils.js create mode 100644 package.json diff --git a/.gitignore b/.gitignore index ca822a63..3b5c49bc 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,6 @@ elm-stuff/ repl-temp-* .DS_Store */index.html -index.html \ No newline at end of file +index.html +node_modules + diff --git a/bin/style-elements.js b/bin/style-elements.js new file mode 100755 index 00000000..d4892c54 --- /dev/null +++ b/bin/style-elements.js @@ -0,0 +1,47 @@ +#!/usr/bin/env node + +var styleElements = require("../"); +var pkg = require("../package.json"); +var program = require("commander"); +var fs = require("fs"); +var chalk = require("chalk"); +var requiredOptions = ["stylesheetModule", "stylesheetFunction", "output"]; +var { writeFile, assertKeysPresent } = require("../js/utils"); + +var { output, stylesheetModule, stylesheetFunction } = getOptions( + process.argv, + program +); + +styleElements({ stylesheetModule, stylesheetFunction }) + .then(result => writeFile(output, result)) + .then(() => { + console.warn( + chalk.green(`\n----> Success! styles were written to ${program.output}\n`) + ); + }); + +function getOptions(argv, program) { + program + .version(pkg.version) + .usage("[options] ") + .option( + "-o, --output [outputFile]", + "(optional) file to write the CSS to", + "out.css" + ) + .parse(argv); + + var options = { + stylesheetModule: program.args[0], + stylesheetFunction: program.args[1], + output: program.output + }; + + assertKeysPresent(options, requiredOptions, _missingOptions => { + program.outputHelp(); + process.exit(1); + }); + + return options; +} diff --git a/index.js b/index.js new file mode 100644 index 00000000..a57803d0 --- /dev/null +++ b/index.js @@ -0,0 +1,76 @@ +var path = require("path"); +var compileElm = require("node-elm-compiler").compile; +var { + unindent, + writeFile, + withTmpDir, + assertKeysPresent +} = require("./js/utils.js"); + +var requiredOptions = ["stylesheetModule", "stylesheetFunction"]; + +function generateCss(opts) { + assertKeysPresent(opts, requiredOptions, missingOptions => { + throw new Error(`Missing options: ${missingOptions.join(", ")}`); + }); + + return withTmpDir().then(tmpDirPath => { + var emitterSourceFile = path.join(tmpDirPath, "StyleElementsEmitter.elm"); + var emitterWorkerFile = path.join(tmpDirPath, "style-elements-emitter.js"); + var emitterTemplate = buildEmitterTemplate( + opts.stylesheetModule, + opts.stylesheetFunction + ); + + return writeFile(emitterSourceFile, emitterTemplate) + .then(() => compile(emitterSourceFile, { output: emitterWorkerFile, yes: true })) + .then(() => extractCssResults(emitterWorkerFile)); + }); +} + +function buildEmitterTemplate(stylesheetModule, stylesheetFunction) { + return unindent( + ` + port module StyleElementsEmitter exposing (..) + + import ${stylesheetModule} + + + port result : String -> Cmd msg + + + stylesheet = + ${stylesheetModule}.${stylesheetFunction} + + + main : Program Never () Never + main = + Platform.program + { init = ( (), result stylesheet.css ) + , update = \\_ _ -> ( (), Cmd.none ) + , subscriptions = \\_ -> Sub.none + } + ` + ); +} + +function compile(src, options) { + return new Promise(function(resolve, reject) { + compileElm(src, options).on("close", function(exitCode) { + if (exitCode === 0) { + resolve(); + } else { + reject("Errored with exit code " + exitCode); + } + }); + }); +} + +function extractCssResults(destFile) { + var emitter = require(destFile).StyleElementsEmitter; + var worker = emitter.worker(); + + return new Promise(resolve => worker.ports.result.subscribe(resolve)); +} + +module.exports = generateCss; diff --git a/js/utils.js b/js/utils.js new file mode 100644 index 00000000..8be1f6b5 --- /dev/null +++ b/js/utils.js @@ -0,0 +1,48 @@ +var tmp = require("tmp"); +var fs = require("fs"); + +module.exports = { + unindent: function(text) { + var indentation = text.split("\n") + .map(line => { + var match = line.match(/([\s]+)[^\s]+/); + return match && match[1]; + }) + .find(value => value); + + return indentation + ? text.replace(new RegExp(`^${indentation}`, "gm"), "") + : text; + }, + + writeFile: function(...args) { + return new Promise((resolve, reject) => { + return fs.writeFile(...args, err => (err ? reject(err) : resolve())); + }); + }, + + withTmpDir: function() { + return new Promise(function(resolve, reject) { + tmp.dir({ unsafeCleanup: true }, function(err, tmpDirPath) { + if (err) { + reject(err); + } else { + resolve(tmpDirPath); + } + }); + }); + }, + + assertKeysPresent: function(object = {}, requiredKeys, missingCallback) { + var providedKeys = Object.keys(object); + var missingKeys = requiredKeys.filter(key => { + return ( + !providedKeys.includes(key) || providedKeys[key] === "" + ); + }); + + if (missingKeys.length > 0) { + missingCallback(missingKeys); + } + } +}; diff --git a/package.json b/package.json new file mode 100644 index 00000000..0b0257fa --- /dev/null +++ b/package.json @@ -0,0 +1,32 @@ +{ + "name": "style-elements", + "version": "3.2.3", + "description": "CSS Preprocoessor for Elm's style-elements", + "main": "index.js", + "bin": { + "style-elements": "./bin/style-elements.js" + }, + "directories": { + "example": "examples", + "test": "tests" + }, + "files": [ + "js/utils.js" + ], + "dependencies": { + "chalk": "^2.0.1", + "commander": "^2.11.0", + "postcss": "^6.0.6", + "tmp": "0.0.31" + }, + "devDependencies": {}, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "http://package.elm-lang.org/packages/mdgriffith/style-elements" + }, + "author": "Matthew Griffith", + "license": "BSD-3-Clause" +}