@sencha/cmd-linux-64
Version:
Productivity and performance optimization tool for building applications with Sencha Ext JS
413 lines (368 loc) • 12.7 kB
JavaScript
const {
Command,
Container,
Help
} = require('switchit');
var JSON5 = require('json5'),
fs = require('fs'),
Path = require('path'),
Fashion = require("./index.js"),
fashionDir = __dirname,
defaultsFile = Path.resolve(fashionDir, 'defaults.json'),
defaults = JSON5.parse(fs.readFileSync(defaultsFile)),
os = require('os'),
platform = os.platform(),
isUnix = platform != 'win32',
fsCanRecurse = (platform == 'win32') || (platform == 'darwin');
global.Fashion = Fashion;
class BaseCompileCommand extends Command {
loadConfig (path) {
var json = fs.readFileSync(path),
base = Object.create(defaults);
return Fashion.merge(base, JSON5.parse(json));
}
loadSaveFile (saveFile) {
var variables = null;
if (saveFile && fs.exists(saveFile)) {
var content = fs.read(saveFile);
if (/\.json$/.test(saveFile)) {
variables = JSON5.parse(content);
}
else {
variables = {};
var regex = /(.*?):(.*?);?$/gim,
matches;
while((matches = regex.exec(content))) {
variables[matches[1]] = matches[2];
}
}
}
return variables;
}
writeFile(fileName, data) {
var dirName = Path.dirname(fileName);
if (!fs.existsSync(dirName)) {
fs.mkdirSync(dirName);
}
fs.writeFileSync(fileName, data);
}
getConfig(params) {
var cfg = params.cfg
? this.loadConfig(params.cfg)
: Object.create(defaults),
cwd = process.cwd(),
input = Path.resolve(cwd, params.input);
// Apply options specified directly to the config, which will have
// been loaded from a file if one was specified, but only apply them
// if not present or when the value in params is not the default
if (!'split' in cfg || params.split !== defaults.split) {
cfg.split = params.split;
}
if (!'compress' in cfg || params.compress !== defaults.compress) {
cfg.compress = params.compress;
}
if (params.saveFile) {
cfg.saveFile = params.saveFile;
}
if (params.srcSavePath) {
cfg.srcSaveFile = params.srcSavePath;
}
// Assign the input file as the file parameter on the to include all
// needed options
cfg.path = input;
if (params.trace) {
Fashion.debugging.trace = true;
}
else {
Fashion.debugging.trace = false;
}
return cfg;
}
getBuilder(params, cfg) {
var builder = new Fashion.Builder({
context: Fashion.merge({
libraries: {
compass: fashionDir + '/lib/compass/stylesheets/',
blueprint: fashionDir + '/lib/blueprint/stylesheets/'
}
}, cfg)
});
if (params.saveFile) {
cfg.variables = this.loadSaveFile(params.saveFile);
}
return builder;
}
runBuild(builder, cfg, params, resolve, reject) {
var error = false,
cwd = process.cwd(),
output = Path.resolve(cwd, params.output),
baseName = Path.dirname(output),
fileName = Path.basename(output),
fnFileName = params.export || (baseName + '/css-vars.js'),
newContent = '',
content, newName, i;
function setError(err) {
error = err;
reject(err);
}
function res(arg) {
var end = new Date().getTime();
var duration = end - start;
Fashion.log("Fashion build completed in " + duration / 1000 + " sec.");
resolve(arg);
}
var start = new Date().getTime();
builder.build(cfg, (css, err, exportFn) => {
var ex = err || builder.exception;
if (ex) {
setError(ex);
return;
}
try {
if (!Array.isArray(css) || css.length === 1) {
if (Array.isArray(css)) {
css = css[0];
}
this.writeFile(output, css);
}
else {
for (i = 0; i < css.length; i++) {
content = css[i];
newName = fileName.replace(/\.css$/g, '_' + (i + 1) + '.css');
this.writeFile(baseName + '/' + newName, content);
newContent += "@import '" + newName + "';\n";
}
this.writeFile(output, newContent);
}
if (exportFn) {
var exportLib = Path.join(fashionDir, "dist", "fashion-export.js");
if (fs.existsSync(exportLib)) {
exportFn = fs.readFileSync(exportLib, {encoding: 'utf8'}) + "\n" + exportFn;
}
this.writeFile(fnFileName, exportFn);
}
res();
} catch (err) {
setError(err);
}
});
}
}
class CompileCommand extends BaseCompileCommand {
execute(params) {
var me = this;
return new Promise((resolve, reject) => {
try {
Fashion.inspect = params.inspect;
if (Fashion.inspect) {
debugger;
}
var cfg = me.getConfig(params),
builder = me.getBuilder(params, cfg);
me.runBuild(builder, cfg, params, resolve, reject);
}
catch (err) {
reject(err);
}
});
}
}
var compileCmdCfg = {
help: {
'': 'Compiles an scss file to a css file.',
trace: 'Enables trace level debugging',
cfg: 'The config file for this compilation pass.',
split: 'The selector count threshold to use for splitting the output into multiple files. -1 to disable.',
compress: 'Enables / disables compressed output.',
saveFile: 'The path to a save.scss file containing variable overrides',
srcSavePath: 'The path to a directory containing generates sass source code.',
input: 'The input css file to compile',
output: 'The output file to generate',
'export': 'The name of the exported CSS Variable processor fn file (if one is generated).'
},
switches: `
[t#trace:boolean=false]
[cfg=]
[c#compress:boolean=false]
[s#split:number=-1]
[saveFile=]
[srcSavePath=]
[i#inspect:boolean=false]`,
parameters: `
input
output
[export=]`
};
CompileCommand.define(compileCmdCfg);
class WatchCommand extends BaseCompileCommand {
queueBuild () {
var me = this;
if (me.timeout) {
clearTimeout(me.timeout);
me.timeout = null;
}
me.timeout = setTimeout(function(){
var runBuild = true;
if (me.filesToInvalidate) {
runBuild = false;
for (var file in me.filesToInvalidate) {
var fullPath = file;
var sassFile = me.filesToInvalidate[file];
var newContent = fs.readFileSync(fullPath, {encoding: 'utf8'});
if (newContent != sassFile.content) {
Fashion.log("Invalidating file : " + fullPath);
sassFile.invalidate();
runBuild = true;
}
}
}
if (runBuild) {
Fashion.log("Fashion re-building...");
me.runBuild(me.builder, me.cfg, me.params, function(){
me.buildComplete();
}, function(err){
me.buildComplete();
});
}
else {
Fashion.log("Fashion waiting for changes...");
}
}, 200);
}
buildComplete () {
var me = this;
clearTimeout(me.timeout);
me.timeout = null;
me.registerMap(me.builder.getFsMap());
Fashion.log("Fashion waiting for changes...");
}
register (entry, recurse) {
var me = this,
builder = me.builder,
watcher = entry.watcher,
base;
if (!watcher) {
base = entry.fullName;
if (isUnix && (base.indexOf('/') !== 0)) {
base = '/' + base;
}
Fashion.trace("Registering path : " + base);
entry.watcher = fs.watch(base, {
recursive: true
},
function(eventType, fileName){
var fullPath = fileName ? Path.resolve(base, fileName) : Path.resolve(base);
fullPath = fullPath.replace(/\\/g, '/');
if (builder.scripts[fullPath]) {
Fashion.trace("change detected in path : " + fullPath);
var sassFile = builder.scripts[fullPath];
me.filesToInvalidate = me.filesToInvalidate || {};
me.filesToInvalidate[fullPath] = sassFile;
me.queueBuild();
}
});
if (recurse && entry.isDir) {
for (var k in entry.files) {
var next = entry.files[k];
if (next.isDir) {
me.register(next, recurse);
}
}
}
}
}
registerMap (map) {
if (map.hasFile) {
this.register(map, !fsCanRecurse);
}
else {
var keys = Object.keys(map.files);
for (var k = 0, len = keys.length; k < len; k++) {
var entry = map.files[keys[k]];
this.registerMap(entry);
}
}
}
execute (params) {
var me = this;
return new Promise(function(resolve, reject){
Fashion.inspect = params.inspect;
if (Fashion.inspect) {
debugger;
}
var cfg = me.getConfig(params),
builder = me.getBuilder(params, cfg),
input = cfg.path,
sassFile, map;
sassFile = builder.getSassFile(input);
sassFile.onReady(function(){
me.builder = builder;
me.params = params;
me.cfg = cfg;
map = builder.getFsMap();
me.registerMap(map);
Fashion.log("Fashion waiting for changes...");
});
});
}
}
var watchCmdCfg = Fashion.apply({}, compileCmdCfg);
watchCmdCfg.help[''] = 'Monitors the file system for changes and rebuilds the specified scss file.';
WatchCommand.define(watchCmdCfg);
class MakeConfigCommand extends Command {
execute(params) {
return new Promise((resolve, reject) => {
fs.readFile(defaultsFile, 'utf8', (err, data) => {
if (err) {
reject(err);
return;
}
fs.writeFile(params.path, data, (err) => {
if (err) {
reject(err);
}
else {
resolve()
}
});
})
});
}
}
MakeConfigCommand.define({
help: 'Generates a default config file.',
parameters: [{
name: 'path',
help: 'The path of the generated config file'
}]
});
class fashion extends Container {
}
fashion.define({
commands: {
compile: CompileCommand,
watch: WatchCommand,
makeConfig: MakeConfigCommand,
help: Help
}
});
new fashion().run().then(
result => {},
reason => {
if (reason.fashionStack && Array.isArray(reason.fashionStack)) {
Fashion.log("Fashion Error : " + reason.message + " :");
var stack = reason.fashionStack,
i = 0,
len = stack.length,
s;
for (; i < len; i++) {
s = stack[i];
Fashion.log(" at " + s[1] + ":" + s[0]);
}
}
else {
console.log(reason);
}
process.exit(1);
}
);