@@ -56,26 +56,13 @@ | |||
"vue-router": "^2.0.0" | |||
}, | |||
"devDependencies": { | |||
"babel-runtime": "^6.26.0", | |||
"chalk": "^2.3.2", | |||
"esbuild": "^0.11.11", | |||
"esbuild-plugin-postcss2": "^0.0.9", | |||
"esbuild-vue": "^0.2.0", | |||
"fast-glob": "^3.2.5", | |||
"graphlib": "^2.1.8", | |||
"http-proxy": "^1.18.1", | |||
"launch-editor": "^2.2.1", | |||
"less": "^3.11.1", | |||
"md5": "^2.3.0", | |||
"rollup": "^1.2.2", | |||
"rollup-plugin-buble": "^0.19.2", | |||
"rollup-plugin-commonjs": "^8.3.0", | |||
"rollup-plugin-multi-entry": "^2.0.2", | |||
"rollup-plugin-node-resolve": "^4.0.1", | |||
"rollup-plugin-postcss": "^2.0.3", | |||
"rollup-plugin-terser": "^4.0.4", | |||
"rollup-plugin-vue": "4.2.0", | |||
"svg-sprite": "^1.5.0", | |||
"yargs": "^16.2.0" | |||
}, | |||
"snyk": true | |||
@@ -1,179 +0,0 @@ | |||
const fs = require('fs'); | |||
const path = require('path'); | |||
const chalk = require('chalk'); | |||
const rollup = require('rollup'); | |||
const { execSync } = require('child_process'); | |||
const log = console.log; // eslint-disable-line | |||
const { | |||
get_build_json, | |||
get_app_path, | |||
apps_list, | |||
run_serially, | |||
assets_path, | |||
sites_path | |||
} = require('./rollup.utils'); | |||
const { | |||
get_options_for, | |||
get_options | |||
} = require('./config'); | |||
const skip_frappe = process.argv.includes("--skip_frappe") | |||
if (skip_frappe) { | |||
let idx = apps_list.indexOf("frappe"); | |||
if (idx > -1) { | |||
apps_list.splice(idx, 1); | |||
} | |||
} | |||
const exists = (flag) => process.argv.indexOf(flag) != -1 | |||
const value = (flag) => (process.argv.indexOf(flag) != -1) ? process.argv[process.argv.indexOf(flag) + 1] : null; | |||
const files = exists("--files") ? value("--files").split(",") : false; | |||
const build_for_app = exists("--app") ? value("--app") : null; | |||
const concat = !exists("--no-concat"); | |||
if (!files) show_production_message(); | |||
ensure_js_css_dirs(); | |||
if (concat) concatenate_files(); | |||
create_build_file(); | |||
if (files) { | |||
build_files(files); | |||
} else if (build_for_app) { | |||
build_assets_for_app(build_for_app) | |||
.then(() => { | |||
run_build_command_for_app(build_for_app); | |||
}) | |||
} else { | |||
build_assets_for_all_apps() | |||
.then(() => { | |||
run_build_command_for_apps() | |||
}); | |||
} | |||
function build_assets_for_all_apps() { | |||
return run_serially( | |||
apps_list.map(app => () => build_assets(app)) | |||
); | |||
} | |||
function build_assets_for_app(app) { | |||
return build_assets(app) | |||
} | |||
function build_from_(options) { | |||
const promises = options.map(({ inputOptions, outputOptions, output_file}) => { | |||
return build(inputOptions, outputOptions) | |||
.then(() => { | |||
log(`${chalk.green('✔')} Built ${output_file}`); | |||
}); | |||
}); | |||
const start = Date.now(); | |||
return Promise.all(promises) | |||
.then(() => { | |||
const time = Date.now() - start; | |||
log(chalk.green(`✨ Done in ${time / 1000}s`)); | |||
}); | |||
} | |||
function build_assets(app) { | |||
const options = get_options_for(app); | |||
if (!options.length) return Promise.resolve(); | |||
log(chalk.yellow(`\nBuilding ${app} assets...\n`)); | |||
return build_from_(options); | |||
} | |||
function build_files(files, app="frappe") { | |||
let ret; | |||
for (let file of files) { | |||
let options = get_options(file, app); | |||
if (!options.length) return Promise.resolve(); | |||
ret += build_from_(options); | |||
} | |||
return ret; | |||
} | |||
function build(inputOptions, outputOptions) { | |||
return rollup.rollup(inputOptions) | |||
.then(bundle => bundle.write(outputOptions)) | |||
.catch(err => { | |||
log(chalk.red(err)); | |||
// Kill process to fail in a CI environment | |||
if (process.env.CI) { | |||
process.kill(process.pid) | |||
} | |||
}); | |||
} | |||
function concatenate_files() { | |||
// only concatenates files, not processed through rollup | |||
const files_to_concat = Object.keys(get_build_json('frappe')) | |||
.filter(filename => filename.startsWith('concat:')); | |||
files_to_concat.forEach(output_file => { | |||
const input_files = get_build_json('frappe')[output_file]; | |||
const file_content = input_files.map(file_name => { | |||
let prefix = get_app_path('frappe'); | |||
if (file_name.startsWith('node_modules/')) { | |||
prefix = path.resolve(get_app_path('frappe'), '..'); | |||
} | |||
const full_path = path.resolve(prefix, file_name); | |||
return `/* ${file_name} */\n` + fs.readFileSync(full_path); | |||
}).join('\n\n'); | |||
const output_file_path = output_file.slice('concat:'.length); | |||
const target_path = path.resolve(assets_path, output_file_path); | |||
fs.writeFileSync(target_path, file_content); | |||
log(`${chalk.green('✔')} Built ${output_file_path}`); | |||
}); | |||
} | |||
function create_build_file() { | |||
const touch = require('touch'); | |||
touch(path.join(sites_path, '.build'), { force: true }); | |||
} | |||
function run_build_command_for_apps() { | |||
let cwd = process.cwd(); | |||
apps_list.map(app => run_build_command_for_app(app)) | |||
process.chdir(cwd); | |||
} | |||
function run_build_command_for_app(app) { | |||
if (app === 'frappe') return; | |||
let root_app_path = path.resolve(get_app_path(app), '..'); | |||
let package_json = path.resolve(root_app_path, 'package.json'); | |||
if (fs.existsSync(package_json)) { | |||
let package = require(package_json); | |||
if (package.scripts && package.scripts.build) { | |||
console.log('\nRunning build command for', chalk.bold(app)); | |||
process.chdir(root_app_path); | |||
execSync('yarn build', { encoding: 'utf8', stdio: 'inherit' }); | |||
} | |||
} | |||
} | |||
function ensure_js_css_dirs() { | |||
const paths = [ | |||
path.resolve(assets_path, 'js'), | |||
path.resolve(assets_path, 'css') | |||
]; | |||
paths.forEach(path => { | |||
if (!fs.existsSync(path)) { | |||
fs.mkdirSync(path); | |||
} | |||
}); | |||
} | |||
function show_production_message() { | |||
const production = process.env.FRAPPE_ENV === 'production'; | |||
log(chalk.yellow(`${production ? 'Production' : 'Development'} mode`)); | |||
} |
@@ -1,239 +0,0 @@ | |||
const path = require('path'); | |||
const fs = require('fs'); | |||
const chalk = require('chalk'); | |||
const log = console.log; // eslint-disable-line | |||
const multi_entry = require('rollup-plugin-multi-entry'); | |||
const commonjs = require('rollup-plugin-commonjs'); | |||
const node_resolve = require('rollup-plugin-node-resolve'); | |||
const postcss = require('rollup-plugin-postcss'); | |||
const buble = require('rollup-plugin-buble'); | |||
const { terser } = require('rollup-plugin-terser'); | |||
const vue = require('rollup-plugin-vue'); | |||
const frappe_html = require('./frappe-html-plugin'); | |||
const less_loader = require('./less-loader'); | |||
const production = process.env.FRAPPE_ENV === 'production'; | |||
const { | |||
apps_list, | |||
assets_path, | |||
bench_path, | |||
get_public_path, | |||
get_app_path, | |||
get_build_json, | |||
get_options_for_scss | |||
} = require('./rollup.utils'); | |||
function get_rollup_options(output_file, input_files) { | |||
if (output_file.endsWith('.js')) { | |||
return get_rollup_options_for_js(output_file, input_files); | |||
} else if(output_file.endsWith('.css')) { | |||
return get_rollup_options_for_css(output_file, input_files); | |||
} | |||
} | |||
function get_rollup_options_for_js(output_file, input_files) { | |||
const node_resolve_paths = [].concat( | |||
// node_modules of apps directly importable | |||
apps_list.map(app => path.resolve(get_app_path(app), '../node_modules')).filter(fs.existsSync), | |||
// import js file of any app if you provide the full path | |||
apps_list.map(app => path.resolve(get_app_path(app), '..')).filter(fs.existsSync) | |||
); | |||
const plugins = [ | |||
// enables array of inputs | |||
multi_entry(), | |||
// .html -> .js | |||
frappe_html(), | |||
// ignore css imports | |||
ignore_css(), | |||
// .vue -> .js | |||
vue.default(), | |||
// ES6 -> ES5 | |||
buble({ | |||
objectAssign: 'Object.assign', | |||
transforms: { | |||
dangerousForOf: true, | |||
classes: false | |||
}, | |||
exclude: [path.resolve(bench_path, '**/*.css'), path.resolve(bench_path, '**/*.less')] | |||
}), | |||
commonjs(), | |||
node_resolve({ | |||
customResolveOptions: { | |||
paths: node_resolve_paths | |||
} | |||
}), | |||
production && terser() | |||
]; | |||
return { | |||
inputOptions: { | |||
input: input_files, | |||
plugins: plugins, | |||
context: 'window', | |||
external: ['jquery'], | |||
onwarn({ code, message, loc, frame }) { | |||
// skip warnings | |||
if (['EVAL', 'SOURCEMAP_BROKEN', 'NAMESPACE_CONFLICT'].includes(code)) return; | |||
if ('UNRESOLVED_IMPORT' === code) { | |||
log(chalk.yellow.underline(code), ':', message); | |||
const command = chalk.yellow('bench setup requirements'); | |||
log(`Cannot find some dependencies. You may have to run "${command}" to install them.`); | |||
log(); | |||
return; | |||
} | |||
if (loc) { | |||
log(`${loc.file} (${loc.line}:${loc.column}) ${message}`); | |||
if (frame) log(frame); | |||
} else { | |||
log(chalk.yellow.underline(code), ':', message); | |||
} | |||
} | |||
}, | |||
outputOptions: { | |||
file: path.resolve(assets_path, output_file), | |||
format: 'iife', | |||
name: 'Rollup', | |||
globals: { | |||
'jquery': 'window.jQuery' | |||
}, | |||
sourcemap: true | |||
} | |||
}; | |||
} | |||
function get_rollup_options_for_css(output_file, input_files) { | |||
const output_path = path.resolve(assets_path, output_file); | |||
const starts_with_css = output_file.startsWith('css/'); | |||
const plugins = [ | |||
// enables array of inputs | |||
multi_entry(), | |||
// less -> css | |||
postcss({ | |||
plugins: [ | |||
starts_with_css ? require('autoprefixer')() : null, | |||
starts_with_css && production ? require('cssnano')({ preset: 'default' }) : null | |||
].filter(Boolean), | |||
extract: output_path, | |||
loaders: [less_loader], | |||
use: [ | |||
['less', { | |||
// import other less/css files starting from these folders | |||
paths: [ | |||
path.resolve(get_public_path('frappe'), 'less') | |||
] | |||
}], | |||
['sass', { | |||
...get_options_for_scss(), | |||
outFile: output_path, | |||
sourceMapContents: true | |||
}] | |||
], | |||
include: [ | |||
path.resolve(bench_path, '**/*.less'), | |||
path.resolve(bench_path, '**/*.scss'), | |||
path.resolve(bench_path, '**/*.css') | |||
], | |||
sourceMap: starts_with_css && !production | |||
}) | |||
]; | |||
return { | |||
inputOptions: { | |||
input: input_files, | |||
plugins: plugins, | |||
onwarn(warning) { | |||
// skip warnings | |||
if (['EMPTY_BUNDLE'].includes(warning.code)) return; | |||
// console.warn everything else | |||
log(chalk.yellow.underline(warning.code), ':', warning.message); | |||
log(warning); | |||
} | |||
}, | |||
outputOptions: { | |||
// this file is always empty, remove it later? | |||
file: path.resolve(assets_path, `css/rollup.manifest.css`), | |||
format: 'cjs' | |||
} | |||
}; | |||
} | |||
function get_options(file, app="frappe") { | |||
const build_json = get_build_json(app); | |||
if (!build_json) return []; | |||
return Object.keys(build_json) | |||
.map(output_file => { | |||
if (output_file === file) { | |||
if (output_file.startsWith('concat:')) return null; | |||
const input_files = build_json[output_file] | |||
.map(input_file => { | |||
let prefix = get_app_path(app); | |||
if (input_file.startsWith('node_modules/')) { | |||
prefix = path.resolve(get_app_path(app), '..'); | |||
} | |||
return path.resolve(prefix, input_file); | |||
}); | |||
return Object.assign( | |||
get_rollup_options(output_file, input_files), { | |||
output_file | |||
}); | |||
} | |||
}) | |||
.filter(Boolean); | |||
} | |||
function get_options_for(app) { | |||
const build_json = get_build_json(app); | |||
if (!build_json) return []; | |||
return Object.keys(build_json) | |||
.map(output_file => { | |||
if (output_file.startsWith('concat:')) return null; | |||
let files = build_json[output_file]; | |||
if (typeof files === 'string') { | |||
files = [files]; | |||
} | |||
const input_files = files | |||
.map(input_file => { | |||
let prefix = get_app_path(app); | |||
if (input_file.startsWith('node_modules/')) { | |||
prefix = path.resolve(get_app_path(app), '..'); | |||
} | |||
return path.resolve(prefix, input_file); | |||
}); | |||
return Object.assign( | |||
get_rollup_options(output_file, input_files), { | |||
output_file | |||
}); | |||
}) | |||
.filter(Boolean); | |||
} | |||
function ignore_css() { | |||
return { | |||
name: 'ignore-css', | |||
transform(code, id) { | |||
if (!['.css', '.scss', '.sass', '.less'].some(ext => id.endsWith(ext))) { | |||
return null; | |||
} | |||
return ` | |||
// ignored ${id} | |||
`; | |||
} | |||
}; | |||
}; | |||
module.exports = { | |||
get_options_for, | |||
get_options | |||
}; |
@@ -1,26 +0,0 @@ | |||
const path = require('path'); | |||
function scrub_html_template(content) { | |||
content = content.replace(/\s/g, ' '); | |||
content = content.replace(/(<!--.*?-->)/g, ''); | |||
return content.replace("'", "\'"); // eslint-disable-line | |||
} | |||
module.exports = function frappe_html() { | |||
return { | |||
name: 'frappe-html', | |||
transform(code, id) { | |||
if (!id.endsWith('.html')) return null; | |||
var filepath = path.basename(id).split('.'); | |||
filepath.splice(-1); | |||
var key = filepath.join("."); | |||
var content = scrub_html_template(code); | |||
return ` | |||
frappe.templates['${key}'] = '${content}'; | |||
`; | |||
} | |||
}; | |||
}; |
@@ -1,54 +0,0 @@ | |||
const pify = require('pify'); | |||
const importCwd = require('import-cwd'); | |||
const path = require('path'); | |||
const getFileName = filepath => path.basename(filepath); | |||
function loadModule(moduleId) { | |||
// Trying to load module normally (relative to plugin directory) | |||
try { | |||
return require(moduleId); | |||
} catch (_) { | |||
// Ignore error | |||
} | |||
// Then, trying to load it relative to CWD | |||
return importCwd.silent(moduleId); | |||
} | |||
module.exports = { | |||
name: 'less', | |||
test: /\.less$/, | |||
async process({ | |||
code | |||
}) { | |||
const less = loadModule('less'); | |||
if (!less) { | |||
throw new Error('You need to install "less" packages in order to process Less files'); | |||
} | |||
let { | |||
css, | |||
map, | |||
imports | |||
} = await pify(less.render.bind(less))(code, { | |||
...this.options, | |||
sourceMap: this.sourceMap && { outputSourceFiles: true }, | |||
filename: this.id | |||
}); | |||
for (const dep of imports) { | |||
this.dependencies.add(dep); | |||
} | |||
if (map) { | |||
map = JSON.parse(map); | |||
map.sources = map.sources.map(source => getFileName(source)); | |||
} | |||
return { | |||
code: css, | |||
map | |||
}; | |||
} | |||
}; |
@@ -1,97 +0,0 @@ | |||
const path = require('path'); | |||
const fs = require('fs'); | |||
const frappe_path = process.cwd(); | |||
const bench_path = path.resolve(frappe_path, '..', '..'); | |||
const sites_path = path.resolve(bench_path, 'sites'); | |||
const apps_list = | |||
fs.readFileSync( | |||
path.resolve(sites_path, 'apps.txt'), { encoding: 'utf-8' } | |||
).split('\n').filter(Boolean); | |||
const assets_path = path.resolve(sites_path, 'assets'); | |||
const app_paths = apps_list.reduce((out, app) => { | |||
out[app] = path.resolve(bench_path, 'apps', app, app) | |||
return out; | |||
}, {}); | |||
const public_paths = apps_list.reduce((out, app) => { | |||
out[app] = path.resolve(app_paths[app], 'public'); | |||
return out; | |||
}, {}); | |||
const public_js_paths = apps_list.reduce((out, app) => { | |||
out[app] = path.resolve(app_paths[app], 'public/js'); | |||
return out; | |||
}, {}); | |||
const bundle_map = apps_list.reduce((out, app) => { | |||
const public_js_path = public_js_paths[app]; | |||
if ( fs.existsSync(public_js_path) ) { | |||
const all_files = fs.readdirSync(public_js_path); | |||
const js_files = all_files.filter(file => file.endsWith('.js')); | |||
for (let js_file of js_files) { | |||
const filename = path.basename(js_file).split('.')[0]; | |||
out[path.join(app, 'js', filename)] = path.resolve(public_js_path, js_file); | |||
} | |||
} | |||
return out; | |||
}, {}); | |||
const get_public_path = app => public_paths[app]; | |||
const get_build_json_path = app => path.resolve(get_public_path(app), 'build.json'); | |||
function get_build_json(app) { | |||
try { | |||
return require(get_build_json_path(app)); | |||
} catch (e) { | |||
// build.json does not exist | |||
return null; | |||
} | |||
} | |||
function delete_file(path) { | |||
if (fs.existsSync(path)) { | |||
fs.unlinkSync(path); | |||
} | |||
} | |||
function run_serially(tasks) { | |||
let result = Promise.resolve(); | |||
tasks.forEach(task => { | |||
if(task) { | |||
result = result.then ? result.then(task) : Promise.resolve(); | |||
} | |||
}); | |||
return result; | |||
} | |||
const get_app_path = app => app_paths[app]; | |||
const get_options_for_scss = () => { | |||
const node_modules_path = path.resolve(get_app_path('frappe'), '..', 'node_modules'); | |||
const app_paths = apps_list.map(get_app_path).map(app_path => path.resolve(app_path, '..')); | |||
return { | |||
includePaths: [ | |||
node_modules_path, | |||
...app_paths | |||
] | |||
}; | |||
}; | |||
module.exports = { | |||
sites_path, | |||
bundle_map, | |||
get_public_path, | |||
get_build_json_path, | |||
get_build_json, | |||
get_app_path, | |||
apps_list, | |||
assets_path, | |||
bench_path, | |||
delete_file, | |||
run_serially, | |||
get_options_for_scss | |||
}; |
@@ -1,102 +0,0 @@ | |||
const path = require('path'); | |||
const chalk = require('chalk'); | |||
const rollup = require('rollup'); | |||
const log = console.log; // eslint-disable-line | |||
const { | |||
apps_list | |||
} = require('./rollup.utils'); | |||
const { | |||
get_options_for | |||
} = require('./config'); | |||
const { get_redis_subscriber } = require('../node_utils'); | |||
const subscriber = get_redis_subscriber(); | |||
watch_assets(); | |||
function watch_assets() { | |||
let watchOptions = []; | |||
apps_list.map(app => { | |||
watchOptions.push(...get_watch_options(app)); | |||
}); | |||
log(chalk.green(`\nRollup Watcher Started`)); | |||
let watcher = rollup.watch(watchOptions); | |||
watcher.on('event', event => { | |||
switch(event.code) { | |||
case 'START': { | |||
log(chalk.yellow(`\nWatching...`)); | |||
break; | |||
} | |||
case 'BUNDLE_START': { | |||
const output = event.output[0]; | |||
if (output.endsWith('.js') || output.endsWith('.vue')) { | |||
log('Rebuilding', path.basename(event.output[0])); | |||
} | |||
break; | |||
} | |||
case 'ERROR': { | |||
log_error(event.error); | |||
break; | |||
} | |||
case 'FATAL': { | |||
log_error(event.error); | |||
break; | |||
} | |||
default: break; | |||
} | |||
}); | |||
} | |||
function get_watch_options(app) { | |||
const options = get_options_for(app); | |||
return options.map(({ inputOptions, outputOptions, output_file}) => { | |||
return Object.assign({}, inputOptions, { | |||
output: outputOptions, | |||
plugins: [log_css_change({output: output_file})].concat(inputOptions.plugins) | |||
}); | |||
}); | |||
} | |||
function log_css_change({output}) { | |||
return { | |||
name: 'log-css-change', | |||
generateBundle() { | |||
if (!output.endsWith('.css')) return null; | |||
log('Rebuilding', path.basename(output)); | |||
return null; | |||
} | |||
}; | |||
} | |||
function log_error(error) { | |||
log(chalk.yellow('Error in: ' + error.id)); | |||
if (error.stack) { | |||
log(chalk.red(error.stack)); | |||
} else { | |||
log(chalk.red(error.toString())); | |||
} | |||
if (error.frame) { | |||
log(chalk.red(error.frame)); | |||
} | |||
// notify redis which in turns tells socketio to publish this to browser | |||
const payload = { | |||
event: 'build_error', | |||
message: ` | |||
Error in: ${error.id} | |||
${error.toString()} | |||
${error.frame ? error.frame : ''} | |||
` | |||
}; | |||
subscriber.publish('events', JSON.stringify(payload)); | |||
} |