1.1.0
Version:
full stack typescript
367 lines (366 loc) • 16.1 kB
JavaScript
const path = require("path");
const fs = require("fs");
const globby = require("globby");
const merge = require("lodash.merge");
const fileExists = require('file-exists');
const nodeExternals = require('webpack-node-externals');
const webpack = require('webpack');
const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin;
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');
const DefinePlugin = require('webpack/lib/DefinePlugin');
const rimraf = require("rimraf");
const mkdirp = require("mkdirp");
const gulp = require("gulp");
const webpackDevServer = require("webpack-dev-server");
const ts = require('gulp-typescript');
const babel = require('gulp-babel');
module.exports = {
isObjectStr: function(str) {
try {
let res = JSON.parse(str);
return res;
} catch (e) {
return false;
}
},
copy: function() {
let me = this;
const obj = {};
return globby([path.join(__dirname, 'copy/**'), path.join(__dirname, 'copy/.**'), "!" + path.join(__dirname, 'copy')])
.then((paths) => {
paths.forEach((filepath, index) => {
let stat = fs.statSync(filepath);
if (stat.isFile()) {
let extname = path.extname(filepath);
let targetpath = path.join(process.cwd(), path.basename(filepath));
let content = (extname == ".js" || extname == ".json") ? require(filepath) : fs.readFileSync(filepath).toString();
let iftargetExists = fileExists.sync(targetpath);
let targetContent = (extname == ".js" || extname == ".json") ? (iftargetExists ? require(targetpath) : {}) : (iftargetExists ? fs.readFileSync(targetpath).toString() : "");
let contentObj = (extname == ".js" || extname == ".json") ? content : me.isObjectStr(content);
let targetObj = (extname == ".js" || extname == ".json") ? targetContent : me.isObjectStr(targetContent);
if ((!!contentObj && !!targetObj) || (!iftargetExists && !!contentObj)) {
let _contnet = iftargetExists ? merge(targetObj, contentObj) : contentObj;
obj[targetpath] = {
value: _contnet,
isObj: true,
isJs: extname == ".js"
};
} else {
obj[targetpath] = {
value: content,
isObj: false,
isJs: extname == ".js"
};
}
}
});
return obj;
});
},
init() {
let me = this;
const path = require("path");
return me.copy().then((obj) => {
// 根据配置建立目录
return obj;
})
},
// 构建前端代码
buildFront(programpath, useserver, port, host) {
let me = this;
if (!programpath) {
console.log("没有指定模块");
return;
} else {
let result = me.analyseProgramPath(programpath, 1);
let webpackConfig = me.initFrontConfig(result);
if (useserver || webpackConfig.hasOwnProperty("devServer")) {
let server = webpackConfig.devServer || {};
let serverConfig = merge({
hot: true,
inline: true,
host: host || "localhost",
port: port || 8899,
stats: { colors: true }
}, server);
for (let entry in webpackConfig.entry) {
webpackConfig.entry[entry] = [webpackConfig.entry[entry]];
webpackConfig.entry[entry].unshift(`webpack-dev-server/client?http://${serverConfig.host}:${serverConfig.port}`, "webpack/hot/dev-server")
};
webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin())
let devServer = new webpackDevServer(webpack(webpackConfig), serverConfig);
devServer.listen(serverConfig.port, serverConfig.host, function() {
console.log(`服务器已启动: http://${serverConfig.host}:${serverConfig.port}`)
})
} else {
// 首先清空目标
rimraf.sync(path.join(result.dest));
webpack(webpackConfig, function(err, stat) {
// rimraf(path.join(result.entry, "tsconfig.json"), () => {})
if (err) console.log(err);
console.log(stat.toString({
chunks: true,
colors: true
}))
})
};
}
},
// 构建后端代码
buildEnd(programpath) {
let me = this;
if (!programpath) {
console.log("没有指定模块");
return;
} else {
let result = me.analyseProgramPath(programpath, 0);
me.compileEnd(result.entry, result.dest);
}
},
// 0 end 1 front
analyseProgramPath(programpath, type) {
let rootConfigPath = path.join(process.cwd(), "fst.config.js")
if (fileExists.sync(rootConfigPath)) {
let rootConfig = require(rootConfigPath);
let srcType = path.join(process.cwd(), path.join(rootConfig.src, type == 1 ? rootConfig.front : rootConfig.end));
let destType = path.join(process.cwd(), path.join(rootConfig.dest, type == 1 ? rootConfig.front : rootConfig.end));
let programEntry = path.join(srcType, programpath);
let programDest = path.join(destType, programpath);
let programConfigPath = path.join(programEntry, "fst.config.js");
let programRootConfig = (rootConfig.hasOwnProperty("programs") && rootConfig.programs.hasOwnProperty(programpath)) ? (type == 1 ? rootConfig.programs[programpath].front : rootConfig.programs[programpath].end) : {};
let programConfig = merge(programRootConfig, fileExists.sync(programConfigPath) ? require(programConfigPath) : {});
return {
entry: programEntry,
dest: programDest,
config: programConfig
}
} else {
console.error(`${rootConfigPath}不存在`);
return null;
}
},
initFrontConfig(result) {
let me = this;
let IS_PROD = process.env.FST_ENV === "production";
let webpackConfig = {
entry: {},
plugins: [],
resolve: {
extensions: ['.ts', ".js"]
},
output: {
path: path.join(result.dest),
filename: '[name][hash].js'
},
module: {
rules: [{
test: /\.json$/,
use: 'json-loader'
}, {
test: /\.html$/,
use: 'raw-loader',
// exclude: [helpers.root('src/index.html')]
}, {
test: /\.(jpg|png|gif)$/,
use: 'file-loader'
}, {
test: /\.(eot|woff2?|svg|ttf)([\?]?.*)$/,
use: 'file-loader'
}]
}
};
// 配置入口
result.config.entry = result.config.hasOwnProperty("entry") ? result.config.entry : [{
src: "polyfills.ts",
name: "polyfills"
}, {
src: "main.ts",
name: "main"
}];
let entry_quene = [];
result.config.entry.forEach((entry, index) => {
entry_quene.push(entry.name);
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
webpackConfig.entry[entry.name] = path.join(result.entry, entry.src);
webpackConfig.plugins.push(new CommonsChunkPlugin({
name: entry.name,
chunks: [entry.name]
}))
});
webpackConfig.plugins.push(new CommonsChunkPlugin({
name: entry_quene.reverse()
}));
// 初始化tsconfig.js
let rootTsconfigPath = path.join(process.cwd(), "tsconfig.json");
let rootTsconfig = fileExists.sync(rootTsconfigPath) ? require(rootTsconfigPath) : {};
if (!result.config.hasOwnProperty("tsconfig")) {
result.config.tsconfig = {};
}
result.config.tsconfig = merge(rootTsconfig, result.config.tsconfig);
// 是否使用babel
result.config.useBabel = result.config.useBabel == undefined ? true : result.config.useBabel;
let tempTsconfigPath = path.join(result.entry, "tsconfig.json");
fs.writeFileSync(tempTsconfigPath, JSON.stringify(result.config.tsconfig), "utf8");
webpackConfig.module.rules.push({
test: /\.ts(x?)$/,
loader: 'awesome-typescript-loader',
query: {
useBabel: result.config.useBabel,
useCache: true,
configFileName: "tsconfig.json"
}
});
// 配置服务器
result.config.server = merge({
enable: false,
config: {
port: 8899,
host: "localhost",
inline: true,
disableHostCheck: true,
hot: true,
historyApiFallback: true,
watchOptions: {
aggregateTimeout: 300,
poll: 1000
}
}
}, result.config.hasOwnProperty("server") ? result.config.server : {});
if (result.config.server.enable) {
webpackConfig.devServer = result.config.server.config;
}
// 配置node
result.config.node = me.mergeProperty(result.config, "node", {
global: true,
crypto: 'empty',
process: true,
module: false,
clearImmediate: false,
setImmediate: false
});
webpackConfig.node = result.config.node;
// 压缩的配置
result.config.uglify = me.mergeProperty(result.config, "uglify", {
enable: IS_PROD,
compress: {
warnings: false,
drop_console: IS_PROD
}
});
result.config.uglify.enable && webpackConfig.plugins.push(new webpack.optimize.UglifyJsPlugin(merge({
compress: {
warnings: false,
drop_console: IS_PROD
},
}, result.config.uglify)));
// 抽取css extractcss
result.config.extractcss = me.mergeProperty(result.config, "extractcss", {
enable: IS_PROD
});
if (result.config.extractcss.enable) {
webpackConfig.module.rules.push({
test: /\.css$/,
loader: ExtractTextPlugin.extract({
use: 'css-loader'
})
});
webpackConfig.module.rules.push({
test: /\.less$/,
loader: ExtractTextPlugin.extract({
use: 'css-loader!sass-loader'
})
});
webpackConfig.module.rules.push({
test: /\.scss$/,
loader: ExtractTextPlugin.extract({
use: 'css-loader!less-loader'
})
});
webpackConfig.plugins.push(new ExtractTextPlugin('[name].[contenthash].css'));
} else {
webpackConfig.module.rules.push({
test: /\.css$/,
use: 'style-loader!css-loader'
});
webpackConfig.module.rules.push({
test: /\.less$/,
use: 'style-loader!css-loader!less-loader'
});
webpackConfig.module.rules.push({
test: /\.scss$/,
use: 'style-loader!css-loade!sass-loaderr'
})
}
// 配置HTML
// webpackonfig.html
result.config.html = me.mergeProperty(result.config, "html", {
template: path.join(result.entry, "index.html"),
// title: "",
chunksSortMode: 'dependency',
// metadata: METADATA,
inject: 'head'
})
webpackConfig.plugins.push(new HtmlWebpackPlugin(result.config.html))
// 配置script加载方法
result.config.script = me.mergeProperty(result.config, "script", {
defaultAttribute: 'defer'
});
webpackConfig.plugins.push(new ScriptExtHtmlWebpackPlugin(result.config.script))
// 变量设置
result.config.variable = me.mergeProperty(result.config, "variable", {});
webpackConfig.plugins.push(new DefinePlugin(result.config.variable));
// 手动扩展
result.config.extend = result.config.extend || function(c) { return c; };
webpackConfig = result.config.extend(webpackConfig);
return webpackConfig;
},
initFrontModule(names) {
let fstConfig = require(path.join(process.cwd(), "fst.config.js"));
let src = path.join(process.cwd(), fstConfig.src);
names.forEach((module, index) => {
let modulepath = path.join(src, path.join("front", module));
// fs.writeFileSync(path.join)
mkdirp.sync(modulepath);
fs.writeFileSync(path.join(modulepath, "main.ts"), '"use strict"', 'utf8');
fs.writeFileSync(path.join(modulepath, "polyfills.ts"), '"use strict"', 'utf8');
fs.writeFileSync(path.join(modulepath, "style.less"), 'body {}', 'utf8');
let html = `<!DOCTYPE html><html><head></head><body>Hello World!</body></html>
`
fs.writeFileSync(path.join(modulepath, "index.html"), html, 'utf8');
fs.writeFileSync(path.join(modulepath, "fst.config.js"), `module.exports = {}`, 'utf8');
fs.writeFileSync(path.join(modulepath, "package.json"), '{}', 'utf8');
})
},
initEndModule(names) {
let fstConfig = require(path.join(process.cwd(), "fst.config.js"));
let src = path.join(process.cwd(), fstConfig.src);
names.forEach((module, index) => {
let modulepath = path.join(src, path.join("end", module));
// fs.writeFileSync(path.join)
mkdirp.sync(modulepath);
fs.writeFileSync(path.join(modulepath, "main.ts"), '"use strict"', 'utf8');
fs.writeFileSync(path.join(modulepath, "fst.config.js"), `module.exports = {}`, 'utf8');
fs.writeFileSync(path.join(modulepath, "package.json"), '{}', 'utf8');
})
},
mergeProperty(obj, key, begin) {
return merge(begin, obj.hasOwnProperty(key) ? obj[key] : {});
},
compileEnd(dest, output) {
let _dest = path.join(dest, "**/**.ts");
// const tsProject = ts.createProject('tsconfig.json');
let rootTsconfig = require(path.join(process.cwd(), "tsconfig.json"));
// let destFstConfig = require(path)
let destFstConfigPath = path.join(dest, "fst.config.js");
let destTsconfig = fileExists(destFstConfigPath) ? require(destFstConfigPath).tsconfig : {};
let config = merge(rootTsconfig, destTsconfig);
rimraf.sync(output);
let stream = gulp.src([_dest, "!node_modules/**/**"]).pipe(ts(config.compilerOptions));
stream
.pipe(gulp.dest(output));
}
}