Skip to content

Instantly share code, notes, and snippets.

@Cheny-chen
Last active February 24, 2020 09:16
Show Gist options
  • Select an option

  • Save Cheny-chen/a35e3d50b898513a0230fa8e19e8ed40 to your computer and use it in GitHub Desktop.

Select an option

Save Cheny-chen/a35e3d50b898513a0230fa8e19e8ed40 to your computer and use it in GitHub Desktop.
/**
* Author: ChenyChen, JCloudYu
* Create: 2020/01/29
**/
(async()=>{
"use strict";
const fs = require('fs');
const path = require('path');
const stream = require('stream');
const readline = require('readline');
const _env_conf = {source_dirs: [], tmpl: null, output: null};
const working_root = process.cwd();
// Read incoming arguments
{
const [, , basic_dir, tmpl_path, ...exec_args] = process.argv;
exec_args.reverse();
if( !basic_dir ){
console.error("Source directory is not assigned!");
process.exit(1);
return;
}
_env_conf.source_dirs.push(path.resolve(working_root, basic_dir));
if( !tmpl_path ){
console.error("Template script is not assigned!");
process.exit(1);
return;
}
_env_conf.tmpl = path.resolve(working_root, tmpl_path);
while( exec_args.length > 0 ){
const opt = exec_args.pop();
switch( opt ){
case "--dir":
case "-d":{
const dpath = exec_args.pop() || '';
if( dpath ){
_env_conf.source_dirs.push(path.resolve(working_root, dpath));
}
break;
}
case "--output":
case "-o":{
const fpath = exec_args.pop() || '';
if( fpath ){
_env_conf.output = path.resolve(working_root, fpath);
}
break;
}
default:{
console.error(`Unknown option \`${ opt }\``);
process.exit(1);
return;
}
}
}
}
// Expose collected arguments
const {tmpl, source_dirs, output} = _env_conf;
// Fetch template
global.BuildTemplate = TemplateResolver;
const template = (()=>{
try{
const script = require(tmpl);
if( !(script instanceof TemplateResolver) ){
console.error("Given tmpl is not a valid template descriptor!");
process.exit(1);
return null;
}
return script;
} catch(e){
if( e.code === 'ENOENT' ){
console.error(`Template path \`${ tmpl }\` doesn't exist!`);
process.exit(1);
return null;
}
throw e;
}
})();
// Check source directories
{
for( const dir_path of source_dirs ){
try{
const dir_stat = fs.statSync(dir_path);
if( !dir_stat.isDirectory() ){
console.error(`Source path \`${ dir_path }\` is not a directory!`);
process.exit(1);
return;
}
} catch(e){
if( e.code === "ENOENT" ){
console.error(`Source path \`${ dir_path }\` is not a directory!`);
process.exit(1);
return;
}
throw e;
}
try{
fs.accessSync(dir_path, fs.constants.R_OK | fs.constants.X_OK);
} catch(e){
console.error(`Current user has no privilege to access directory \`${ dir_path }\`!`);
process.exit(1);
return;
}
}
}
// Create output stream
const write_stream = (()=>{
if( !output ) return process.stdout;
try{
return fs.createWriteStream(output, {mode: 0o644, flags: 'w'});
} catch(e){
return null;
}
})();
if( !write_stream ){
console.error(`Cannot create output stream to file \`${ output }\`!`);
process.exit(1);
return;
}
// Collect and parse all js script of the target folders
const named_map = {}, unnamed_pool = [];
for await( const dir_path of source_dirs ){
const arrayFiles = await ReadMainFolder(dir_path);
for await ( const file of arrayFiles ){
await ParseFile(file, named_map, unnamed_pool);
}
}
console.log(named_map, unnamed_pool);
// Output generated script base on template script
{
const {statics, dynamics} = template;
await WriteToStream(write_stream, statics[0]);
for( let i = 1; i < statics.length; i++ ){
const dynamic_content = dynamics[i - 1];
if( typeof dynamic_content !== "string" ){
await WriteToStream(write_stream, '' + dynamic_content);
await WriteToStream(write_stream, statics[i]);
continue;
}
const important = dynamic_content[0] === '!';
const optional = dynamic_content[0] === '?';
const key = (important || optional) ? dynamic_content.substring(1) : dynamic_content;
const code_segment = named_map[key];
if( code_segment !== undefined ){
await WriteToStream(write_stream, code_segment);
}
else if( important ){
console.error(`Missing required named block \`${ key }\``);
process.exit(1);
}
else{
if( !optional ){
console.error(`Missm matching named block \`${ key }\``);
}
await WriteToStream(write_stream, '');
}
await WriteToStream(write_stream, statics[i]);
}
for( const code_segment of unnamed_pool ){
await WriteToStream(write_stream, code_segment);
}
await CloseStream(write_stream);
}
function TemplateResolver(strings, ...dynamics) {
if ( !(this instanceof TemplateResolver) ) {
return new TemplateResolver(strings, ...dynamics);
}
this.statics = strings;
this.dynamics = dynamics;
}
function WriteToStream(stream, data){
return new Promise((resolve, reject)=>{
const should_wait = stream.write(data, ()=>resolve(true));
if( !should_wait ){
reject(false);
}
});
}
function CloseStream(stream){
return new Promise((resolve, reject)=>stream.end(resolve));
}
async function ReadMainFolder(folder, arrayFiles = [], arrayFolder = []){
fs.readdirSync(folder, {withFileTypes: true}).forEach(filename=>{
if( filename.isDirectory() && !/^\..*|test*/.test(filename.name) ){
arrayFolder.push(path.join(folder, filename.name));
}
if( !filename.isDirectory() && filename.name.indexOf('.js') > 0 ){
arrayFiles.push(path.join(folder, filename.name));
}
});
if( arrayFolder.length > 0 ){
for await(const foldername of arrayFolder) {
const temp = await ReadMainFolder(foldername, arrayFiles);
arrayFiles.concat(temp);
}
}
return arrayFiles
}
async function ReadLines(filePath) {
return new Promise( async(resolve, reject) => {
const output = new stream.PassThrough({ objectMode: true });
const readInterface = readline.createInterface({
input: fs.createReadStream(filePath),
});
readInterface.on("line", line => {
output.write(line);
});
readInterface.on("close", () => {
output.push(null);
});
resolve(output);
})
}
async function ParseFile(filePath, named, unnamed){
return new Promise( async(resolve, reject) => {
const readInterface = await ReadLines(filePath);
let flag = false, splitByEqualsSign = [], temp_unamed = '', index=0;
for await(let line of readInterface) {
if( line.indexOf('//@export') > -1 ) {
splitByEqualsSign = line.split('=');
line = '\n';
flag = true;
}
else if( line.indexOf('//@endexport') > -1 ){
line = line.replace('//@endexport', '');
flag = false;
if( temp_unamed ){
unnamed.push(temp_unamed);
temp_unamed = '';
}
}
if( flag ){
line = line + '\n';
if( splitByEqualsSign[1] && !named.hasOwnProperty(splitByEqualsSign[1]) ){
named[splitByEqualsSign[1]] = '';
}
else if( splitByEqualsSign.length > 1 && splitByEqualsSign[1] ){
named[splitByEqualsSign[1]] += line;
}
else if( line !== '\n' ){
temp_unamed += line;
}
}
}
resolve(true);
})
}
})()
.catch((e)=>setTimeout(()=>{throw e;}));
@Cheny-chen
Copy link
Author

Cheny-chen commented Jan 28, 2020

node embedded-script-packer.js <folder name> <template filename> -d <destination filename>

@JCloudYu
Copy link

node embedded-script-packer.js <folder name> <template filename> -d <destination filename>

打錯了喔!是
node embedded-script-packer.js <source folder path> <template file path> [-d <additional source folder path>] [-o <output file path>]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment