@extjs/reactor
Version:
Use Ext JS components in React.
201 lines (181 loc) • 6.28 kB
JavaScript
const parseArgs = require('minimist'),
fs = require('fs'),
path = require('path'),
sencha = require('@extjs/sencha-cmd'),
cjson = require('comment-json'),
{ fork } = require('child_process'),
mkdirp = require('mkdirp');
console.log('USING sencha: ', sencha);
// A skeleton for a ext-react workspace.json file.
const workspaceJson = {
apps: [],
frameworks: { ext: '../node_modules/@extjs/ext-react' },
build: { dir: '${workspace.dir}/build' },
packages: { dir: '${workspace.dir}/packages,${workspace.dir}/../node_modules/@extjs' },
properties: {
'build.web.root': '${workspace.dir}/../'
}
};
/**
* Prints usage for using this CLI.
*/
printUsage = () => {
console.log(
`
ext-react CLI currently offers a method for generating a theme for ext-react apps (SASS based system).
An example usage is:
ext-react generate theme --baseTheme theme-material --name my-awesome-theme
Options
* --name -n - The name of the theme package.
* --baseTheme -b - (Optional) The theme to base your custom theme off of. Defaults to 'theme-material'.
`
);
}
/**
* Ensures a 'ext-react/packages' folder exists for the workspace and theme packages to be installed in.
*/
const ensurePackagesFolder = () => {
return new Promise(resolve => {
const dir = path.join('.', 'ext-react', 'packages');
fs.stat(dir, (err, stats) => {
if(err || !stats.isDirectory()) {
mkdirp(dir, resolve.bind(null));
} else {
resolve();
}
})
});
}
/**
* Generates a workspace in the current directory (by writing a workspace.json file).
*/
const generateWorkspace = () => {
return ensurePackagesFolder().then(() => {
return new Promise((resolve, reject) => {
if(!workspaceExists()) {
console.log('Generating Sencha workspace...');
fs.writeFile(path.join('.', 'ext-react', 'workspace.json'), JSON.stringify(workspaceJson, null, 4), err => {
if(err) return reject(err);
return resolve();
});
} else {
console.log('Using existing workspace at ext-react/workspace.json');
return resolve();
}
});
});
}
/**
* Checks if a workspace exists in the current directory.
* @returns Boolean
*/
const workspaceExists = () => {
try {
return fs.statSync(path.join('.', 'ext-react', 'workspace.json')).isFile();
} catch(e) {
return false;
}
}
/**
* Generates a theme package with provided arguments in config object (name and baseTheme).
* @param {*} config
*/
const generateTheme = config => {
console.log('Generating theme package...');
return new Promise((resolve, reject) => {
const proc = fork(sencha, [
'generate', 'package',
'--type', 'THEME',
'--extend', config.baseTheme || 'theme-material',
'--framework', 'ext',
'--name', config.name
], { cwd: path.join('.', 'ext-react'), silent: true });
proc.once('exit', code => code > 0 ? reject(`Generating package failed with code: ${code}`) : resolve());
proc.stdout.pipe(process.stdout);
proc.stderr.pipe(process.stderr);
return proc;
}).then(updatePackageJson.bind(null, config));
}
/**
* Set's the Sass Namespace to "" and toolkit to "modern" in package.json, this is to help compatiblity with Sencha Themer.
*/
const updatePackageJson = config => {
const packageJsonPath = path.join('.', 'ext-react', 'packages', config.name, 'package.json');
return new Promise((resolve, reject) => {
fs.readFile(packageJsonPath, 'utf-8', (err, data) => {
if(err) return reject(`Could not read package.json for theme named: ${config.name}`);
return resolve(cjson.parse(data));
});
}).then(data => {
(data.sencha || data).sass.namespace = '';
(data.sencha || data).toolkit = 'modern';
return data;
}).then(data => {
return new Promise((resolve, reject) => {
fs.writeFile(packageJsonPath, cjson.stringify(data, null, 4), err => {
if(err) return reject(`Could not write package.json for theme named: ${config.name}`);
return resolve();
});
});
});
}
/**
* Applies a theme based on `name` property in config object to current app by writing to a .sencharc file.
*/
const applyTheme = config => {
console.log('Applying theme to current app...');
return new Promise((resolve, reject) => {
fs.writeFile('.ext-reactrc', JSON.stringify({
theme: path.join('.', 'ext-react', 'packages', config.name)
}, null, 4), err => {
if(err) return reject(err);
else return resolve();
});
});
}
// Parse the arguments passed from command-line using minimist.
const args = parseArgs(process.argv.slice(2), {
string: ['name', 'baseTheme'],
boolean: ['apply'],
default: { baseTheme: 'theme-material' },
alias: {
baseTheme: ['base', 'b'],
name: 'n',
apply: 'a'
}
});
/**
* Evaluate the first 2 arguments to see what to do.
*/
switch(args._.join(' ')) {
case 'generate workspace': {
return generateWorkspace(args);
}
case 'generate theme': {
if(!args.name) {
console.error('Missing required argument: --name');
return printUsage();
}
return generateWorkspace(args)
.then(generateTheme.bind(null, args))
.then((args.apply ? applyTheme.bind(null, args) : Promise.resolve([])))
.then(() => {
console.log(`Theme created at: ext-react/packages/${args.name}`);
})
.catch(error => {
console.error('Error encountered.', error);
})
}
case 'apply theme': {
if(!args.name) {
console.error('Missing required argument: --name');
return printUsage();
}
return applyTheme(args);
}
default: {
// TODO: Print usage here.
printUsage();
}
}