UNPKG

@sencha/cmd-linux-64

Version:

Productivity and performance optimization tool for building applications with Sencha Ext JS

413 lines (368 loc) 12.7 kB
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); } );