Skip to content

Commit

Permalink
生成应用端类型文件
Browse files Browse the repository at this point in the history
  • Loading branch information
yafoo committed Feb 22, 2024
1 parent ab40a62 commit 79f0772
Show file tree
Hide file tree
Showing 4 changed files with 263 additions and 3 deletions.
6 changes: 3 additions & 3 deletions lib/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@ class App extends Koa

// types
if(cfg_app.app_debug) {
if(require('./utils/fs').isFileSync(path.join(cfg_app.base_dir, 'jsconfig.json'))) {
require('./types')();
}
const {isFileSync} = require('./utils/fs');
const jsconfigFile = path.join(cfg_app.base_dir, 'jsconfig.json');
isFileSync(jsconfigFile) && require('./types')();
}

// server
Expand Down
22 changes: 22 additions & 0 deletions lib/tpl/types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
module.exports = `/**
* @type {typeof import('jj.js/types')}
*/
const JJCtx = require('jj.js').Ctx;
/**
* @typedef {import('jj.js/types').Config} JJConfig
*/
__TYPES__
/**
* @class Ctx
*/
class Ctx extends JJCtx {
__PROPERTY__
}
/**
* @module types
*/
module.exports = Ctx;`;
235 changes: 235 additions & 0 deletions lib/types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,235 @@
const path = require('path');
const fs = require('fs');
const {app: cfg_app} = require('./config');
const toHump = require('./utils/str').toHump;
const Logger = require('./logger');

/**
* watch
*/
function start() {
const watch = require('watch');
const filter = function(f, stat) {
const static_dir = cfg_app.static_dir ? path.join(cfg_app.base_dir, cfg_app.static_dir) : '';
return (!~f.indexOf('.') || /\.js(on)?$/.test(f)) && !~f.indexOf('types.js') && (static_dir && !~f.indexOf(static_dir));
};

let prevFile = {file: null,action: null,stat: null};
watch.watchTree(cfg_app.base_dir, {ignoreDotFiles: true, ignoreDirectoryPattern: /node_modules/, filter}, (f, curr, prev) => {
if(typeof f == "object" && prev === null && curr === null) {
// Finished walking the tree
Object.keys(f).forEach(file => {
Logger.system("wacth:", file);
});
} else if(prev === null) {
// f is a new file
if(prevFile.file != f || prevFile.action != "created") {
prevFile = {file: f, action: "created", stat: curr};
createFile(f);
}
} else if(curr.nlink === 0) {
// f was removed
if(prevFile.file != f || prevFile.action != "removed") {
prevFile = { file: f, action: "removed", stat: curr };
createFile(f);
}
} else {
// f was changed
if(prevFile.file === null) {
createFile(f);
} else {
// stat might return null, so catch errors
try {
if (prevFile.stat.mtime.getTime() !== curr.mtime.getTime()) {
createFile(f);
}
} catch(e) {
createFile(f);
}
}
}
});
}

/**
* createTypesFile
*/
function createFile(f) {
if(this._f == f) {
return;
}
this._f = f;

const f_info = f.replace(cfg_app.base_dir + path.sep, '').split(path.sep);
if(f_info[0] && f_info[0] != cfg_app.common_app && !~f_info[0].indexOf('.')) {
this._APP = f_info[0];
}

const ignore = ['node_modules', 'docker', 'package-lock.json', 'types.js'];
ignore.push(path.basename(module.parent.parent.parent.filename));
cfg_app.static_dir && ignore.push(path.join(cfg_app.base_dir, cfg_app.static_dir));

const node_list = getNodeList('.', ignore);
calcNodeList(node_list, this._APP);

const types_tpl = require('./tpl/types');
const types_str = types_tpl.replace('__TYPES__', createTypes(node_list)).replace('__PROPERTY__', createProps(node_list));
fs.writeFile('types.js', types_str, function(err) {
if(err) {
Logger.system('createTypes error:', err);
} else {
Logger.system('createTypes success:', path.join(cfg_app.base_dir, 'types.js'));
}
});
}

// 生成类型定义
function createTypes(node_list, base_type = '') {
let types = '';
let props = '';
Object.entries(node_list).forEach(([path, node]) => {
const type_name = toHump(base_type + '_' + node.node_name);
if(node.file_type == 'dir') {
types += createTypes(node.children, type_name);
} else {
if(!node.is_class) {
types += `/**\n * @typedef {typeof import('${path}')} ${type_name}\n */\n\n`;
} else {
types += `/**\n * @typedef {typeof import('${path}')} ${type_name}Class\n`;
types += ` * @typedef {typeof import('${path}').prototype} ${type_name}Instance\n`;
types += ` * @typedef {(${type_name}Class & ${type_name}Instance)} ${type_name}\n */\n\n`;
}
}

props += ` * @property {${type_name}} ${node.node_name} - ${path}\n`;
});

if(base_type) {
types += `/**\n * @typedef {object} ${base_type}\n${props} */\n\n`;
}

return types;
}

// 生成智能属性
function createProps(node_list) {
let props = '';

Object.entries(node_list).forEach(([path, node]) => {
const type_name = toHump('_' + node.node_name);
if(type_name != 'Config') {
props += `\n /** @type {${type_name}} */\n $${node.node_name}\n`;
}
});

let type_config = 'JJConfig';
if(node_list['./config']) {
type_config = '(JJConfig & Config)';
}
props += `\n /** @type {${type_config}} */\n $config\n`;

return props;
}

// 获取节点对象
function getNodeList(dir, ignore=['node_modules', 'docker', 'package-lock.json']) {
const isClass = require('is-class');
const files = fs.readdirSync(path.join(cfg_app.base_dir, dir), {withFileTypes: true});
const type_list = {};
files.forEach(dirent => {
const file_name = dirent.name;
const file_path = dir + '/' + file_name;
const node_name = path.parse(file_name).name;
const abs_path = path.join(cfg_app.base_dir, file_path);
if(ignore.filter(n => abs_path.includes(n)).length) {
return;
}

const file_type = dirent.isFile() ? 'file' : dirent.isDirectory() ? 'dir' : '';
const regFile = /.+\.js(on)?$/.test(file_name);
const regDir = !file_name.includes('.');
if(file_type == 'file' && !regFile) {
return;
}
if(file_type == 'dir' && !regDir) {
return;
}

if(file_type == 'file') {
try {
// @ts-ignore
type_list[file_path] = {node_name, file_name, file_type, is_class: isClass(require(abs_path))};
} catch(e) {
// 包含语法错误时,require会出错
}
} else if(file_type == 'dir') {
type_list[file_path] = {node_name, file_name, file_type, children: getNodeList(file_path, ignore)};;
}

});
return type_list;
}

// 复制子节点
function copyNodeList(node_list) {
const copy_node_list = {};
Object.entries(node_list).forEach(([path, node]) => {
copy_node_list[path] = {...node};
if(node.children) {
copy_node_list[path].children = copyNodeList(node.children);
}
});
return copy_node_list;
}

// 处理common库
function calcNodeList(node_list, _APP) {
// 处理common
const node_common = './' + cfg_app.common_app;
if(cfg_app.common_app && node_list[node_common] && node_list[node_common].children) {
const children = copyNodeList(node_list[node_common].children);
Object.entries(children).forEach(([path, node]) => {
node_list[path] = node;
});
}

// 处理app
const node_app = './' + _APP;
if(_APP && node_list[node_app] && node_list[node_app].children) {
Object.entries(node_list[node_app].children).forEach(([path, node]) => {
const path_common = path.replace(node_app, node_common);
if(!node_list[path_common]) {
node_list[path] = node;
} else if(node_list[path_common].file_type == 'file') {
delete node_list[path_common];
node_list[path] = node;
} else if(node.children) {
Object.entries(node.children).forEach(([p, n]) => {
const p_commmon = p.replace(node_app, node_common);
node_list[path_common].children[p_commmon] && delete node_list[path_common].children[p_commmon];
node_list[path_common].children[p] = n;
});
}
});
}

// 特殊处理view和response
const node_view = './' + _APP + '/view';
const common_view = './' + cfg_app.common_app + '/view';
if(node_list[node_view] && !node_list[node_view].is_class) {
delete node_list[node_view];
}
if(node_list[common_view] && !node_list[common_view].is_class) {
delete node_list[common_view];
}
const node_response = './' + _APP + '/response';
const common_response = './' + cfg_app.common_app + '/response';
if(node_list[node_response] && !node_list[node_response].is_class) {
delete node_list[node_response];
}
if(node_list[common_response] && !node_list[common_response].is_class) {
delete node_list[common_response];
}
}

module.exports = start;
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@
"koa-static": "^5.0.0",
"mysql": "^2.18.1"
},
"devDependencies": {
"watch": "^1.0.2"
},
"engines": {
"node": ">= 12.17.0"
}
Expand Down

0 comments on commit 79f0772

Please sign in to comment.