Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CLI - generates css and writes to a file #41

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@ elm-stuff/
repl-temp-*
.DS_Store
*/index.html
index.html
index.html
node_modules

56 changes: 56 additions & 0 deletions bin/style-elements.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/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", "mode", "output"];
var utils = require("../js/utils");
var writeFile = utils.writeFile;
var assertKeysPresent = utils.assertKeysPresent;

var options = getOptions(process.argv, program);

styleElements({
stylesheetModule: options.stylesheetModule,
stylesheetFunction: options.stylesheetFunction,
mode: options.mode
})
.then(result => writeFile(options.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] <stylesheetModule> <stylesheetFunction>")
.option(
"-o, --output [outputFile]",
"(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,
mode: program.mode
};

assertKeysPresent(options, requiredOptions, _missingOptions => {
program.outputHelp();
process.exit(1);
});

return options;
}
86 changes: 86 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
var path = require("path");
var compileElm = require("node-elm-compiler").compile;
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", "mode"];

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,
opts.mode
);

return writeFile(emitterSourceFile, emitterTemplate)
.then(() => compile(emitterSourceFile, { output: emitterWorkerFile, yes: true }))
.then(() => extractCssResults(emitterWorkerFile));
});
}

function buildEmitterTemplate(stylesheetModule, stylesheetFunction, mode) {
return unindent(
`
port module StyleElementsEmitter exposing (..)

import ${stylesheetModule}
import Element


port result : String -> Cmd msg


styles =
Element.${renderFunction(mode)} ${stylesheetModule}.${stylesheetFunction}


main : Program Never () Never
main =
Platform.program
{ init = ( (), result styles )
, update = \\_ _ -> ( (), Cmd.none )
, subscriptions = \\_ -> Sub.none
}
`
);
}

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) {
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;
48 changes: 48 additions & 0 deletions js/utils.js
Original file line number Diff line number Diff line change
@@ -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(path, content) {
return new Promise((resolve, reject) => {
return fs.writeFile(path, content, 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.indexOf(key) === -1 || providedKeys[key] === ""
);
});

if (missingKeys.length > 0) {
missingCallback(missingKeys);
}
}
};
28 changes: 28 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -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"
}
18 changes: 17 additions & 1 deletion src/Element.elm
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ module Element
, layout
, viewport
, toHtml
, toLayoutCss
, toViewportCss
, embedStylesheet
, Device
, classifyDevice
Expand Down Expand Up @@ -196,7 +198,7 @@ Some convient elements for working with forms.

## Advanced Rendering

@docs toHtml, embedStylesheet
@docs toHtml, embedStylesheet, toLayoutCss, toViewportCss


### Deprecated
Expand Down Expand Up @@ -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
Expand Down
14 changes: 12 additions & 2 deletions src/Element/Internal/Render.elm
Original file line number Diff line number Diff line change
Expand Up @@ -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
]


Expand Down