Skip to content

Commit

Permalink
Issue #386 [Enhancement][WIP] targetConfig as gulp.registry(); gulp t…
Browse files Browse the repository at this point in the history
…asks instead of npm scripts
  • Loading branch information
t2ym committed Sep 7, 2020
1 parent fbeeb69 commit eff90e0
Show file tree
Hide file tree
Showing 4 changed files with 506 additions and 111 deletions.
274 changes: 189 additions & 85 deletions demo-config/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,100 +4,204 @@ Copyright (c) 2020 Tetsuya Mori <t2y3141592@gmail.com>. All rights reserved.
*/
const path = require('path');
const fs = require('fs');
const gulp = require('gulp');
const GulpDefaultRegistry = require('undertaker-registry');

// get thin-hook package path even from within thin-hook package
const hookPath = (() => {
try {
let hookJs = require.resolve('thin-hook');
if (hookJs) {
return path.dirname(hookJs);
class TargetConfig extends GulpDefaultRegistry {
constructor() {
super();
}
init(gulpInst) {
super.init(gulpInst);
this.gulp = gulpInst;
this._configure();
this._resolveConfig();
}
get(name) {
let task = super.get(name);
if (!task) {
task = this.task(name); // register plugin
}
return task;
}
catch (e) {}
let dirname = __dirname;
let packageName;
while (!packageName) {
let packagePath = path.resolve(dirname, 'package.json');
if (fs.existsSync(packagePath)) {
let package = require(packagePath);
packageName = package.name;
break;
set(name, fn) {
let boundFn = fn.bind(this);
boundFn.displayName = fn.displayName;
boundFn.description = fn.description;
boundFn.flags = fn.flags;
return super.set(name, boundFn);
}
// register gulp task
// Note: this function must be called via task() so that this is the targetConfig object
task(pluginName) {
const targetConfig = this;
const configuratorPath = path.resolve(targetConfig.path.hook, targetConfig.path.plugins, pluginName, 'configurator.js');
const plugin = require(configuratorPath);
if (plugin.name !== pluginName) {
throw new Error(`task("${pluginName}"): plugin.name === ${plugin.name} does not match`);
}
if (typeof plugin.init === 'function') {
plugin.init(targetConfig);
}
dirname = path.resolve(dirname, '..');
if (dirname === '/') {
break;
}
return this.gulp.task(pluginName,
this.gulp.series(
Object.assign((done) => {
if (Array.isArray(plugin.dependencies)) {
plugin.dependencies.forEach(dependency => {
if(!(targetConfig[dependency] && targetConfig[dependency].done)) {
throw new Error(`plugin task "${pluginName}": dependent task "${dependency}" has not completed`);
}
});
}
done();
}, { displayName: `${pluginName} check dependencies` }),
Object.assign(plugin.configurator(targetConfig), { displayName: `${pluginName} configurator` }),
Object.assign((done) => {
targetConfig[pluginName] = targetConfig[pluginName] || {};
targetConfig[pluginName].done = true;
done();
}, { displayName: `${pluginName} done` }),
)
);
}
if (packageName === 'thin-hook') {
return dirname;
// get thin-hook package path even from within thin-hook package
static hookPath = (() => {
try {
let hookJs = require.resolve('thin-hook');
if (hookJs) {
return path.dirname(hookJs);
}
}
catch (e) {}
let dirname = __dirname;
let packageName;
while (!packageName) {
let packagePath = path.resolve(dirname, 'package.json');
if (fs.existsSync(packagePath)) {
let _package = require(packagePath);
packageName = _package.name;
break;
}
dirname = path.resolve(dirname, '..');
if (dirname === '/') {
break;
}
}
if (packageName === 'thin-hook') {
return dirname;
}
else {
return path.resolve(__dirname, '..');
}
})();
static needResolution = Symbol('need resolution');
static basePath = module.parent.path;
static configPath = __dirname;
// delayed resolution of configurations
_resolveConfig() {
for (let config in this) {
if (this[config] && typeof this[config] === 'object' && this[config][TargetConfig.needResolution]) {
for (let key in this[config]) {
if (typeof this[config][key] === 'function') {
this[config][key] = this[config][key].call(this);
}
}
}
}
}
else {
return path.resolve(__dirname, '..');
// generate arguments for concurrently
getConcurrentlyArguments(commands, ...names) {
let args = names.map(command => `"${commands[command]}"`);
return `--names "${names.join(',')}" ${args.join(' ')}`;
}
})();

const basePath = module.parent.path;
const configPath = __dirname;
const plugins = 'plugins';

// register gulp task
// Note: this function must be called via targetConfig.task() so that this is the targetConfig object
const task = function (pluginName) {
const targetConfig = this;
const configuratorPath = path.resolve(targetConfig.path.hook, targetConfig.path.plugins, pluginName, 'configurator.js');
const plugin = require(configuratorPath);
if (plugin.name !== pluginName) {
throw new Error(`targetConfig.task("${pluginName}"): plugin.name === ${plugin.name} does not match`);
// configure itself step by step
_configure() {
this.path = {
base: TargetConfig.basePath,
root: 'demo',
config: path.relative(TargetConfig.basePath, TargetConfig.configPath),
backend: 'demo-backend',
frontend: 'demo-frontend',
keys: 'demo-keys',
encodedIndexHtml: 'index.html',
decodedIndexHtml: 'original-index.html',
hook: TargetConfig.hookPath,
plugins: 'plugins',
};
Object.assign(this, { // dependent on this.path
url: {
mappings: [
// [ fullPath, urlPath ] in directory path names
[path.resolve(this.path.base, 'bower_components'), '/components'], // highest priority in mapping
[path.resolve(this.path.base, 'demo'), '/components/thin-hook/demo'], // path.resolve(targetConfig.path.base, targetConfig.path.root) is interpreted as root
[this.path.hook, '/components/thin-hook'], // for hook.min.js
],
},
server: {
serverJs: 'demoServer.js',
port: 8080,
devToolsHostPort: '0.0.0.0:9229',
concurrency: 4,
},
errorReportService: {
port: 8081,
},
validationService: {
port: 8082,
},
certificates: {
generateCertSh: 'generate_cert.sh',
CA: 'demoCA',
},
mode: {
enableDebugging: false,
devtoolsDisabled: true,
},
});
Object.assign(this, {
commands: { // dependent on this.path, this.server, this.certificates, and this.commands itself
[TargetConfig.needResolution]: true,
"http": () => `concurrently -k -s all -c cyan,magentaBright,yellowBright \\
${this.getConcurrentlyArguments(this.commands, 'httpServer', 'errorReportService', 'validationService')}`,
"debug": () => `concurrently -k -s all -c cyan,magentaBright,yellowBright \\
${this.getConcurrentlyArguments(this.commands, 'debugServer', 'errorReportService', 'validationService')}`,
"https": () => `concurrently -k -s all -c cyan,magentaBright,yellowBright \\
${this.getConcurrentlyArguments(this.commands, 'httpsServer', 'errorReportService', 'validationService')}`,
"cache-bundle": () => `concurrently -k --kill-others-on-fail -s first -c cyan,magentaBright,yellowBright \\
${this.getConcurrentlyArguments(this.commands, 'buildServer', 'cacheBundleUploadService', 'cacheBundleGeneration')}`,
"updateHtmlHash": () => `concurrently -k --kill-others-on-fail -s first -c cyan,magentaBright,yellowBright \\
${this.getConcurrentlyArguments(this.commands, 'buildServer', 'cacheBundleUploadService', 'loadOnly')}`,
"buildServer": `node ${path.resolve(this.path.base, this.path.backend, this.server.serverJs)} -p ${this.server.port} \\
-m build -P https -H localhost:${this.server.port}`,
"httpServer": `node ${path.resolve(this.path.base, this.path.backend, this.server.serverJs)} -p ${this.server.port} \\
-m server -c ${this.server.concurrency} -H ${process.env['SERVER_HOST'] || 'localhost'}`,
"httpsServer": `node ${path.resolve(this.path.base, this.path.backend, this.server.serverJs)} -p ${this.server.port} \\
-m server -c ${this.server.concurrency} -P https -H ${process.env['SERVER_HOST'] || 'localhost'}:${this.server.port}`,
"debugServer": `node --inspect-brk=${this.server.devToolsHostPort} ${path.resolve(this.path.base, this.path.backend, this.server.serverJs)} -p ${this.server.port} \\
-m debug -c 1 -H ${process.env['SERVER_HOST'] || 'localhost'}`,
"postHtml": () => `concurrently -k -s all -c cyan,magentaBright,yellowBright \\
${this.getConcurrentlyArguments(this.commands, 'postHtmlServer', 'errorReportService', 'validationService')}`,
"postHtmlServer": `node ${path.resolve(this.path.base, this.path.backend, this.server.serverJs)} -p ${this.server.port} \\
-m server -c ${this.server.concurrency} -H ${process.env['SERVER_HOST'] || 'localhost'} --middleware ${path.resolve(this.path.base, this.path.backend, 'postHtml.js')}`,
"errorReportService": `node ${path.resolve(this.path.base, this.path.backend, 'errorReportService.js')} -p ${this.errorReportService.port}`,
"validationService": `node ${path.resolve(this.path.base, this.path.backend, 'validationService.js')} -p ${this.validationService.port} \\
-m server -H ${process.env['VALIDATION_HOST'] || 'localhost'}`,
"integrity-service-helpers": `cd ${path.resolve(this.path.base, this.path.backend, 'integrity-service-helpers')} && npm install`,
"validation-console": `cd ${path.resolve(this.path.base, this.path.backend, 'validation-console')} && npm ci && npm run build`,
"certificates": `cd ${path.resolve(this.path.base, this.path.keys)} && ./${this.certificates.generateCertSh} `,
"clean-demo-certificates": `cd ${path.resolve(this.path.base, this.path.keys)} && rm -riv ${this.certificates.CA}`,
"cacheBundleUploadService": `node ${path.resolve(this.path.base, this.path.backend, 'cacheBundleUploadService.js')}`,
"cacheBundleGeneration": `node ${path.resolve(this.path.base, this.path.backend, 'cacheBundleGeneration.js')}`,
"loadOnly": `node ${path.resolve(this.path.base, this.path.backend, 'cacheBundleGeneration.js')} loadOnly`,
"test:attack": () => `concurrently -k -s all -c cyan,magentaBright,yellowBright \\
${this.getConcurrentlyArguments(this.commands, 'buildServer', 'cacheBundleUploadService', 'puppeteerAttackTest')}`,
"puppeteerAttackTest": `node ${path.resolve(this.path.base, 'test/puppeteerAttackTest.js')}`,
"demo-frontend-modules": `cd ${path.resolve(this.path.base, this.path.root)} && npm install`,
"demo-frontend-modules-locked": `cd ${path.resolve(this.path.base, this.path.root)} && npm ci`,
},
});
}
return gulp.task(pluginName,
gulp.series(
Object.assign((done) => {
if (Array.isArray(plugin.dependencies)) {
plugin.dependencies.forEach(dependency => {
if(!(targetConfig[dependency] && targetConfig[dependency].done)) {
throw new Error(`plugin task "${pluginName}": dependent task "${dependency}" has not completed`);
}
});
}
done();
}, { displayName: `${pluginName} check dependencies` }),
Object.assign(plugin.configurator(targetConfig), { displayName: `${pluginName} configurator` }),
Object.assign((done) => {
targetConfig[pluginName] = targetConfig[pluginName] || {};
targetConfig[pluginName].done = true;
done();
}, { displayName: `${pluginName} done` }),
)
);
}

const targetConfig = {
task: task,
path: {
base: basePath,
root: 'demo',
config: path.relative(basePath, configPath),
backend: 'demo-backend',
frontend: 'demo-frontend',
keys: 'demo-keys',
encodedIndexHtml: 'index.html',
decodedIndexHtml: 'original-index.html',
hook: hookPath,
plugins: plugins,
},
url: {
mappings: [
// [ fullPath, urlPath ] in directory path names
[path.resolve(basePath, 'bower_components'), '/components'], // highest priority in mapping
[path.resolve(basePath, 'demo'), '/components/thin-hook/demo'], // path.resolve(targetConfig.path.base, targetConfig.path.root) is interpreted as root
[hookPath, '/components/thin-hook'], // for hook.min.js
],
},
mode: {
enableDebugging: false,
devtoolsDisabled: true,
},
};
const targetConfig = new TargetConfig();

module.exports = targetConfig;

0 comments on commit eff90e0

Please sign in to comment.