From daba5effd712b8f7e6e809a74a0b069dccab3faf Mon Sep 17 00:00:00 2001 From: opsb Date: Sat, 15 Jul 2017 11:09:01 +0200 Subject: [PATCH 1/3] 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 | 28 ++++++++++++++++ 5 files changed, 202 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..2e24450d --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "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" + }, + "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" +} From 0480034ceb3c0dd6b9fa00d439fc969f40e59288 Mon Sep 17 00:00:00 2001 From: opsb Date: Sat, 15 Jul 2017 16:45:03 +0200 Subject: [PATCH 2/3] Add node 4 compatibility --- bin/style-elements.js | 16 +++++++++------- index.js | 12 ++++++------ js/utils.js | 10 +++++----- 3 files changed, 20 insertions(+), 18 deletions(-) diff --git a/bin/style-elements.js b/bin/style-elements.js index d4892c54..7d192b1f 100755 --- a/bin/style-elements.js +++ b/bin/style-elements.js @@ -6,15 +6,17 @@ var program = require("commander"); var fs = require("fs"); var chalk = require("chalk"); var requiredOptions = ["stylesheetModule", "stylesheetFunction", "output"]; -var { writeFile, assertKeysPresent } = require("../js/utils"); +var utils = require("../js/utils"); +var writeFile = utils.writeFile; +var assertKeysPresent = utils.assertKeysPresent; -var { output, stylesheetModule, stylesheetFunction } = getOptions( - process.argv, - program -); +var options = getOptions(process.argv, program); -styleElements({ stylesheetModule, stylesheetFunction }) - .then(result => writeFile(output, result)) +styleElements({ + stylesheetModule: options.stylesheetModule, + stylesheetFunction: options.stylesheetFunction +}) + .then(result => writeFile(options.output, result)) .then(() => { console.warn( chalk.green(`\n----> Success! styles were written to ${program.output}\n`) diff --git a/index.js b/index.js index a57803d0..fdc72677 100644 --- a/index.js +++ b/index.js @@ -1,11 +1,11 @@ var path = require("path"); var compileElm = require("node-elm-compiler").compile; -var { - unindent, - writeFile, - withTmpDir, - assertKeysPresent -} = require("./js/utils.js"); +var utils = require("./js/utils.js"); + +var unindent = utils.unindent; +var writeFile = utils.writeFile; +var withTmpDir = utils.withTmpDir; +var assertKeysPresent = utils.assertKeysPresent; var requiredOptions = ["stylesheetModule", "stylesheetFunction"]; diff --git a/js/utils.js b/js/utils.js index 8be1f6b5..1549c828 100644 --- a/js/utils.js +++ b/js/utils.js @@ -15,9 +15,9 @@ module.exports = { : text; }, - writeFile: function(...args) { + writeFile: function(path, content) { return new Promise((resolve, reject) => { - return fs.writeFile(...args, err => (err ? reject(err) : resolve())); + return fs.writeFile(path, content, err => (err ? reject(err) : resolve())); }); }, @@ -33,11 +33,11 @@ module.exports = { }); }, - assertKeysPresent: function(object = {}, requiredKeys, missingCallback) { - var providedKeys = Object.keys(object); + assertKeysPresent: function(object, requiredKeys, missingCallback) { + var providedKeys = Object.keys(object || {}); var missingKeys = requiredKeys.filter(key => { return ( - !providedKeys.includes(key) || providedKeys[key] === "" + providedKeys.indexOf(key) === -1 || providedKeys[key] === "" ); }); From 0c4ff2ff2186f59cfbd0a47795be4c2972687916 Mon Sep 17 00:00:00 2001 From: opsb Date: Sat, 15 Jul 2017 18:43:53 +0200 Subject: [PATCH 3/3] Include css normalize in style output --- bin/style-elements.js | 13 ++++++++++--- index.js | 22 ++++++++++++++++------ src/Element.elm | 18 +++++++++++++++++- src/Element/Internal/Render.elm | 14 ++++++++++++-- 4 files changed, 55 insertions(+), 12 deletions(-) diff --git a/bin/style-elements.js b/bin/style-elements.js index 7d192b1f..4656a828 100755 --- a/bin/style-elements.js +++ b/bin/style-elements.js @@ -5,7 +5,7 @@ var pkg = require("../package.json"); var program = require("commander"); var fs = require("fs"); var chalk = require("chalk"); -var requiredOptions = ["stylesheetModule", "stylesheetFunction", "output"]; +var requiredOptions = ["stylesheetModule", "stylesheetFunction", "mode", "output"]; var utils = require("../js/utils"); var writeFile = utils.writeFile; var assertKeysPresent = utils.assertKeysPresent; @@ -14,7 +14,8 @@ var options = getOptions(process.argv, program); styleElements({ stylesheetModule: options.stylesheetModule, - stylesheetFunction: options.stylesheetFunction + stylesheetFunction: options.stylesheetFunction, + mode: options.mode }) .then(result => writeFile(options.output, result)) .then(() => { @@ -32,12 +33,18 @@ function getOptions(argv, program) { "(optional) file to write the CSS to", "out.css" ) + .option( + "-m, --mode [layout/viewport]", + "(optional) whether to render stylesheet for 'layout' or 'viewport'", + "layout" + ) .parse(argv); var options = { stylesheetModule: program.args[0], stylesheetFunction: program.args[1], - output: program.output + output: program.output, + mode: program.mode }; assertKeysPresent(options, requiredOptions, _missingOptions => { diff --git a/index.js b/index.js index fdc72677..246c4527 100644 --- a/index.js +++ b/index.js @@ -7,7 +7,7 @@ var writeFile = utils.writeFile; var withTmpDir = utils.withTmpDir; var assertKeysPresent = utils.assertKeysPresent; -var requiredOptions = ["stylesheetModule", "stylesheetFunction"]; +var requiredOptions = ["stylesheetModule", "stylesheetFunction", "mode"]; function generateCss(opts) { assertKeysPresent(opts, requiredOptions, missingOptions => { @@ -19,7 +19,8 @@ function generateCss(opts) { var emitterWorkerFile = path.join(tmpDirPath, "style-elements-emitter.js"); var emitterTemplate = buildEmitterTemplate( opts.stylesheetModule, - opts.stylesheetFunction + opts.stylesheetFunction, + opts.mode ); return writeFile(emitterSourceFile, emitterTemplate) @@ -28,25 +29,26 @@ function generateCss(opts) { }); } -function buildEmitterTemplate(stylesheetModule, stylesheetFunction) { +function buildEmitterTemplate(stylesheetModule, stylesheetFunction, mode) { return unindent( ` port module StyleElementsEmitter exposing (..) import ${stylesheetModule} + import Element port result : String -> Cmd msg - stylesheet = - ${stylesheetModule}.${stylesheetFunction} + styles = + Element.${renderFunction(mode)} ${stylesheetModule}.${stylesheetFunction} main : Program Never () Never main = Platform.program - { init = ( (), result stylesheet.css ) + { init = ( (), result styles ) , update = \\_ _ -> ( (), Cmd.none ) , subscriptions = \\_ -> Sub.none } @@ -54,6 +56,14 @@ function buildEmitterTemplate(stylesheetModule, stylesheetFunction) { ); } +function renderFunction(mode) { + switch (mode) { + case "viewport": return "toViewportCss"; + case "layout": return "toLayoutCss"; + default: throw new Error(`Invalid mode: ${mode}, must be either 'layout' or 'viewport'`); + } +} + function compile(src, options) { return new Promise(function(resolve, reject) { compileElm(src, options).on("close", function(exitCode) { diff --git a/src/Element.elm b/src/Element.elm index a939371f..b8469393 100644 --- a/src/Element.elm +++ b/src/Element.elm @@ -62,6 +62,8 @@ module Element , layout , viewport , toHtml + , toLayoutCss + , toViewportCss , embedStylesheet , Device , classifyDevice @@ -196,7 +198,7 @@ Some convient elements for working with forms. ## Advanced Rendering -@docs toHtml, embedStylesheet +@docs toHtml, embedStylesheet, toLayoutCss, toViewportCss ### Deprecated @@ -1252,6 +1254,20 @@ toHtml stylesheet el = (Render.render stylesheet el) +{-| Renders the stylesheet generated by 'layout' to css +-} +toLayoutCss : StyleSheet style variation -> String +toLayoutCss stylesheet = + Render.layoutCss stylesheet + + +{-| Renders the stylesheet generated by 'viewport' to css +-} +toViewportCss : StyleSheet style variation -> String +toViewportCss stylesheet = + Render.viewportCss stylesheet + + {-| Embed a stylesheet. -} embedStylesheet : StyleSheet style variation -> Html msg diff --git a/src/Element/Internal/Render.elm b/src/Element/Internal/Render.elm index 4517944f..2c3defb2 100644 --- a/src/Element/Internal/Render.elm +++ b/src/Element/Internal/Render.elm @@ -45,15 +45,25 @@ root stylesheet elm = (embed False stylesheet :: render stylesheet elm) +viewportCss : Internal.StyleSheet elem variation -> String +viewportCss stylesheet = + normalizeFull () ++ stylesheet.css + + +layoutCss : Internal.StyleSheet elem variation -> String +layoutCss stylesheet = + normalize ++ stylesheet.css + + embed : Bool -> Internal.StyleSheet elem variation -> Html msg embed full stylesheet = Html.node "style" [] [ Html.text <| if full then - normalizeFull () ++ stylesheet.css + viewportCss stylesheet else - normalize ++ stylesheet.css + layoutCss stylesheet ]