mirror of
https://github.com/frappe/books.git
synced 2025-02-02 20:18:26 +00:00
Frappe CLI for development
- frappe start command - starts frappejs server - sets up webpack middleware to bundle files - introduce frappe.conf.js for configuration
This commit is contained in:
parent
cf00d26c91
commit
ddd9a786d2
12
cli.js
12
cli.js
@ -2,9 +2,19 @@
|
||||
|
||||
const program = require('commander');
|
||||
const process = require('process');
|
||||
const package = require('./package.json');
|
||||
const boilerplate = require('frappejs/model/boilerplate');
|
||||
|
||||
program.command('new-model <name>')
|
||||
program
|
||||
.version(package.version)
|
||||
|
||||
program
|
||||
.command('start [mode]')
|
||||
.description('Start development server')
|
||||
.action(require('./webpack/start'))
|
||||
|
||||
program
|
||||
.command('new-model <name>')
|
||||
.description('Create a new model in the `models/doctype` folder')
|
||||
.action((name) => {
|
||||
boilerplate.make_model_files(name);
|
||||
|
21
package.json
21
package.json
@ -3,24 +3,35 @@
|
||||
"version": "0.0.8",
|
||||
"description": "Frappe.js",
|
||||
"main": "index.js",
|
||||
"bin": "cli.js",
|
||||
"bin": {
|
||||
"frappe": "cli.js"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha tests",
|
||||
"start": "nodemon app.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"awesomplete": "^1.1.2",
|
||||
"babel-core": "^6.26.3",
|
||||
"babel-loader": "^7.1.5",
|
||||
"bcrypt": "^2.0.1",
|
||||
"body-parser": "^1.18.2",
|
||||
"bootstrap": "^4.1.2",
|
||||
"case-sensitive-paths-webpack-plugin": "^2.1.2",
|
||||
"codemirror": "^5.35.0",
|
||||
"commander": "^2.13.0",
|
||||
"cors": "^2.8.4",
|
||||
"css-loader": "^1.0.0",
|
||||
"deepmerge": "^2.1.0",
|
||||
"electron": "2.0.5",
|
||||
"electron-debug": "^2.0.0",
|
||||
"electron-devtools-installer": "^2.2.4",
|
||||
"express": "^4.16.2",
|
||||
"feather-icons": "^4.7.3",
|
||||
"flatpickr": "^4.3.2",
|
||||
"frappe-datatable": "^1.1.2",
|
||||
"friendly-errors-webpack-plugin": "^1.7.0",
|
||||
"html-webpack-plugin": "^3.2.0",
|
||||
"jquery": "^3.3.1",
|
||||
"jwt-simple": "^0.5.1",
|
||||
"luxon": "^1.0.0",
|
||||
@ -34,12 +45,18 @@
|
||||
"passport": "^0.4.0",
|
||||
"passport-jwt": "^4.0.0",
|
||||
"puppeteer": "^1.2.0",
|
||||
"sass-loader": "^7.0.3",
|
||||
"showdown": "^1.8.6",
|
||||
"socket.io": "^2.0.4",
|
||||
"sqlite3": "^3.1.13",
|
||||
"vue": "^2.5.16",
|
||||
"vue-flatpickr-component": "^7.0.4",
|
||||
"vue-router": "^3.0.1"
|
||||
"vue-loader": "^15.2.6",
|
||||
"vue-router": "^3.0.1",
|
||||
"vue-template-compiler": "^2.5.16",
|
||||
"webpack": "^4.16.1",
|
||||
"webpack-dev-server": "^3.1.4",
|
||||
"webpack-hot-middleware": "^2.22.3"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -15,7 +15,10 @@ const bodyParser = require('body-parser');
|
||||
const fs = require('fs');
|
||||
const { setupExpressRoute: setRouteForPDF } = require('frappejs/server/pdf');
|
||||
const auth = require('./../auth/auth')();
|
||||
const morgan = require('morgan')
|
||||
const morgan = require('morgan');
|
||||
const { addWebpackMiddleware } = require('../webpack/serve');
|
||||
const { getAppConfig } = require('../webpack/utils');
|
||||
const appConfig = getAppConfig();
|
||||
|
||||
require.extensions['.html'] = function (module, filename) {
|
||||
module.exports = fs.readFileSync(filename, 'utf8');
|
||||
@ -35,7 +38,6 @@ module.exports = {
|
||||
// app
|
||||
app.use(bodyParser.json());
|
||||
app.use(bodyParser.urlencoded({ extended: true }));
|
||||
app.use(express.static(staticPath));
|
||||
app.use(morgan('tiny'));
|
||||
|
||||
if (connectionParams.enableCORS) {
|
||||
@ -53,7 +55,12 @@ module.exports = {
|
||||
// routes
|
||||
restAPI.setup(app);
|
||||
|
||||
frappe.config.port = connectionParams.port || 8000;
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
// webpack dev server
|
||||
addWebpackMiddleware(app);
|
||||
}
|
||||
|
||||
frappe.config.port = appConfig.dev.devServerPort
|
||||
|
||||
// listen
|
||||
server.listen(frappe.config.port, () => {
|
||||
|
114
webpack/config.js
Normal file
114
webpack/config.js
Normal file
@ -0,0 +1,114 @@
|
||||
const path = require('path');
|
||||
const webpack = require('webpack');
|
||||
const { getAppConfig, resolveAppDir } = require('./utils');
|
||||
|
||||
const plugins = {
|
||||
NamedModules: webpack.NamedModulesPlugin,
|
||||
HotModuleReplacement: webpack.HotModuleReplacementPlugin,
|
||||
Define: webpack.DefinePlugin,
|
||||
Progress: webpack.ProgressPlugin,
|
||||
VueLoader: require('vue-loader/lib/plugin'),
|
||||
Html: require('html-webpack-plugin'),
|
||||
CaseSensitivePaths: require('case-sensitive-paths-webpack-plugin'),
|
||||
FriendlyErrors: require('friendly-errors-webpack-plugin'),
|
||||
}
|
||||
|
||||
const appConfig = getAppConfig();
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
|
||||
function getConfig() {
|
||||
const config = {
|
||||
mode: isProduction ? 'production' : 'development',
|
||||
context: resolveAppDir(),
|
||||
entry: appConfig.dev.entry,
|
||||
output: {
|
||||
path: path.resolve(appConfig.dev.outputDir),
|
||||
filename: '[name].js',
|
||||
publicPath: appConfig.dev.assetsPublicPath
|
||||
},
|
||||
devtool: 'cheap-module-eval-source-map',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.vue$/,
|
||||
loader: 'vue-loader'
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
loader: 'babel-loader',
|
||||
exclude: file => (
|
||||
/node_modules/.test(file) &&
|
||||
!/\.vue\.js/.test(file)
|
||||
)
|
||||
},
|
||||
{
|
||||
test: /\.css$/,
|
||||
use: [
|
||||
'vue-style-loader',
|
||||
'css-loader'
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
use: [
|
||||
'vue-style-loader',
|
||||
'css-loader',
|
||||
'sass-loader'
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.js', '.vue'],
|
||||
alias: {
|
||||
'vue$': 'vue/dist/vue.esm.js',
|
||||
'deepmerge$': 'deepmerge/dist/umd.js'
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
new plugins.Define({
|
||||
'process.env': appConfig.dev.env
|
||||
}),
|
||||
new plugins.VueLoader(),
|
||||
new plugins.Html({
|
||||
template: resolveAppDir('src/index.html')
|
||||
}),
|
||||
new plugins.CaseSensitivePaths(),
|
||||
new plugins.NamedModules(),
|
||||
new plugins.HotModuleReplacement(),
|
||||
new plugins.FriendlyErrors({
|
||||
compilationSuccessInfo: {
|
||||
messages: [`FrappeJS server started at http://${appConfig.dev.devServerHost}:${appConfig.dev.devServerPort}`],
|
||||
},
|
||||
}),
|
||||
new plugins.Progress()
|
||||
],
|
||||
optimization: {
|
||||
noEmitOnErrors: false
|
||||
},
|
||||
devServer: {
|
||||
contentBase: './dist',
|
||||
hot: true,
|
||||
quiet: true
|
||||
},
|
||||
node: {
|
||||
// prevent webpack from injecting useless setImmediate polyfill because Vue
|
||||
// source contains it (although only uses it if it's native).
|
||||
setImmediate: false,
|
||||
// process is injected via DefinePlugin, although some 3rd party
|
||||
// libraries may require a mock to work properly (#934)
|
||||
process: 'mock',
|
||||
// prevent webpack from injecting mocks to Node native modules
|
||||
// that does not make sense for the client
|
||||
dgram: 'empty',
|
||||
fs: 'empty',
|
||||
net: 'empty',
|
||||
tls: 'empty',
|
||||
child_process: 'empty'
|
||||
}
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
module.exports = getConfig;
|
19
webpack/logger.js
Normal file
19
webpack/logger.js
Normal file
@ -0,0 +1,19 @@
|
||||
const ms = require('ms');
|
||||
const chalk = require('chalk');
|
||||
|
||||
let prevTime;
|
||||
|
||||
module.exports = function (banner, color = 'green') {
|
||||
return function (message) {
|
||||
const currentTime = +new Date();
|
||||
const diff = currentTime - (prevTime || currentTime);
|
||||
prevTime = currentTime;
|
||||
|
||||
if (message) {
|
||||
console.log(` ${chalk[color](banner)} ${message} ${chalk.green(`+${ms(diff)}`)}`)
|
||||
}
|
||||
else {
|
||||
console.log()
|
||||
}
|
||||
}
|
||||
}
|
74
webpack/serve.js
Normal file
74
webpack/serve.js
Normal file
@ -0,0 +1,74 @@
|
||||
const webpack = require('webpack');
|
||||
const webpackDevServer = require('webpack-dev-server');
|
||||
const webpackDevMiddleware = require('webpack-dev-middleware');
|
||||
const webpackHotMiddleware = require('webpack-hot-middleware');
|
||||
|
||||
const logger = require('./logger');
|
||||
const { getAppConfig, resolveAppDir } = require('./utils');
|
||||
const getWebpackConfig = require('./config');
|
||||
|
||||
const log = logger('serve');
|
||||
const warn = logger('serve', 'red');
|
||||
|
||||
const appConfig = getAppConfig();
|
||||
const webpackConfig = getWebpackConfig();
|
||||
|
||||
function addWebpackMiddleware(app) {
|
||||
log();
|
||||
log('Starting dev server...');
|
||||
|
||||
addWebpackEntryPoints(webpackConfig);
|
||||
const compiler = webpack(webpackConfig);
|
||||
|
||||
app.use(webpackDevMiddleware(compiler, {
|
||||
logLevel: 'silent',
|
||||
publicPath: webpackConfig.output.publicPath
|
||||
}));
|
||||
|
||||
app.use(webpackHotMiddleware(compiler, {
|
||||
path: '/__webpack_hmr'
|
||||
}));
|
||||
}
|
||||
|
||||
function startWebpackDevServer() {
|
||||
log();
|
||||
log('Starting dev server...');
|
||||
|
||||
return new Promise(resolve => {
|
||||
addWebpackEntryPoints(webpackConfig, true);
|
||||
const compiler = webpack(webpackConfig);
|
||||
const server = new webpackDevServer(compiler, webpackConfig.devServer);
|
||||
|
||||
const { devServerHost, devServerPort } = appConfig.dev;
|
||||
server.listen(devServerPort, devServerHost, () => {
|
||||
// listening on devServerPort
|
||||
|
||||
compiler.hooks.done.tap('webpack done compiling', function() {
|
||||
resolve(server);
|
||||
});
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function addWebpackEntryPoints(webpackConfig, forDevServer) {
|
||||
const devServerEntryPoints = [
|
||||
resolveAppDir('node_modules/webpack-dev-server/client/index.js') + '?http://localhost',
|
||||
'webpack/hot/dev-server'
|
||||
];
|
||||
const middlewareEntryPoints = [
|
||||
'webpack-hot-middleware/client?path=/__webpack_hmr'
|
||||
];
|
||||
const entryPoints = forDevServer ? devServerEntryPoints : middlewareEntryPoints;
|
||||
const entry = webpackConfig.entry;
|
||||
|
||||
Object.keys(entry).forEach(key => {
|
||||
entry[key] = [...entryPoints, entry[key]];
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
module.exports = {
|
||||
addWebpackMiddleware,
|
||||
startWebpackDevServer
|
||||
}
|
25
webpack/start.js
Normal file
25
webpack/start.js
Normal file
@ -0,0 +1,25 @@
|
||||
const { spawn } = require('child_process');
|
||||
const { startWebpackDevServer } = require('./serve');
|
||||
const { getAppConfig, resolveAppDir } = require('./utils');
|
||||
const appConfig = getAppConfig();
|
||||
|
||||
module.exports = function start(mode) {
|
||||
process.env.NODE_ENV = 'development';
|
||||
|
||||
if (mode === 'electron') {
|
||||
const electron = require('electron');
|
||||
const electronPaths = appConfig.electron.paths;
|
||||
|
||||
startWebpackDevServer()
|
||||
.then((devServer) => {
|
||||
const p = spawn(electron, [resolveAppDir(electronPaths.mainDev)], { stdio: 'inherit' })
|
||||
p.on('close', () => {
|
||||
devServer.close();
|
||||
});
|
||||
});
|
||||
} else {
|
||||
const nodePaths = appConfig.node.paths;
|
||||
|
||||
spawn('node', [resolveAppDir(nodePaths.main)], { stdio: 'inherit' })
|
||||
}
|
||||
}
|
44
webpack/utils.js
Normal file
44
webpack/utils.js
Normal file
@ -0,0 +1,44 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const chalk = require('chalk');
|
||||
const deepmerge = require('deepmerge');
|
||||
const logger = require('./logger');
|
||||
|
||||
const frappeConf = 'frappe.conf.js';
|
||||
|
||||
function getAppDir() {
|
||||
let dir = process.cwd();
|
||||
|
||||
if (fs.existsSync(path.join(dir, frappeConf))) {
|
||||
return dir;
|
||||
}
|
||||
|
||||
warn = logger('utils', 'red')
|
||||
|
||||
warn();
|
||||
warn(`Looks like this is not the root of a FrappeJS project`);
|
||||
warn(`Please run this command from a folder which contains ${chalk.yellow(frappeConf)} file`);
|
||||
warn();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function getAppConfig() {
|
||||
const defaults = {
|
||||
dev: {
|
||||
devServerHost: 'localhost',
|
||||
devServerPort: 8000
|
||||
}
|
||||
}
|
||||
const appConfig = require(path.resolve(getAppDir(), frappeConf));
|
||||
return deepmerge(defaults, appConfig);
|
||||
}
|
||||
|
||||
function resolveAppDir(...args) {
|
||||
return path.resolve(getAppDir(), ...args);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getAppDir,
|
||||
getAppConfig,
|
||||
resolveAppDir
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user