Skip to content

Instantly share code, notes, and snippets.

@Cheny-chen
Last active February 24, 2020 09:16
Show Gist options
  • Save Cheny-chen/a35e3d50b898513a0230fa8e19e8ed40 to your computer and use it in GitHub Desktop.
Save Cheny-chen/a35e3d50b898513a0230fa8e19e8ed40 to your computer and use it in GitHub Desktop.

Revisions

  1. Cheny-chen revised this gist Feb 24, 2020. 1 changed file with 38 additions and 27 deletions.
    65 changes: 38 additions & 27 deletions embedded-script-packer.js
    Original file line number Diff line number Diff line change
    @@ -20,7 +20,7 @@
    const _env_conf = {source_dirs: [], skipped_paths:[], tmpl: null, output: null};
    const working_root = process.cwd();


    let line_sum=0;
    // Read incoming arguments
    {
    const [, , basic_dir, tmpl_path, ...exec_args] = process.argv;
    @@ -182,8 +182,7 @@
    await ParseFile(file, named_map, unnamed_pool);
    }
    }




    console.error(`\nRendering...`);
    // Output generated script base on template script
    @@ -253,10 +252,11 @@
    }
    function WriteToStream(stream, data){
    return new Promise((resolve, reject)=>{
    const should_wait = stream.write(data, ()=>resolve(true));
    if( !should_wait ){
    reject(false);
    }
    stream.write(data, ()=>resolve(true));

    // if( !should_wait ){
    // reject(false);
    // }
    });
    }
    function CloseStream(stream){
    @@ -284,18 +284,21 @@
    return arrayFiles;
    }
    function GenerateLineReader(filePath) {
    const output = new stream.PassThrough({objectMode:true});
    const readInterface = readline.createInterface({
    input: fs.createReadStream(filePath),
    return new Promise((resolve)=>{
    let lineNumer=0;
    const output = new stream.PassThrough({objectMode:true, highWaterMark:1000});
    const readInterface = readline.createInterface({
    input: fs.createReadStream(filePath),
    });
    readInterface.on("line", line => {
    output.write(line);
    lineNumer++;
    });
    readInterface.on("close", () => {
    output.push(null);
    resolve( {readInterface:output, lineNumer:lineNumer} );
    });
    });
    readInterface.on("line", line => {
    output.write(line);
    });
    readInterface.on("close", () => {
    output.push(null);
    });

    return output;
    }
    async function ParseFile(filePath, named, unnamed){
    let idx1, idx2;
    @@ -305,12 +308,17 @@
    let line_count=0;
    let line_queue=[];

    const readInterface = GenerateLineReader(filePath);

    const { readInterface, lineNumer} = await GenerateLineReader(filePath);

    for await(const new_line of readInterface) {
    console.log(new_line);

    line_sum++;
    line_queue.push(new_line);
    ProcessLineQueue();
    ProcessLineQueue();
    if(lineNumer===line_sum) {
    line_sum=0;
    return;
    }
    }

    // Do remaining cleanup on boundary conditions
    @@ -328,7 +336,7 @@



    function ProcessLineQueue() {
    async function ProcessLineQueue() {
    while(line_queue.length > 0) {
    let line = line_queue.shift();
    line_count++;
    @@ -343,6 +351,7 @@
    if ( line[idx1+START_SIG_LEN] === '=' ) {
    name_key = line.substring(idx1+START_SIG_LEN+1).trim();


    // Purge key identifiers
    if (name_key[0] === '!' || name_key[0] === '@' || name_key === '?') {
    name_key = name_key.substring(1);
    @@ -358,13 +367,14 @@
    // Search for paired //@endexport
    const has_terminator = ((idx1 = line.indexOf(END_SIG)) >= 0);
    const has_starter = ((idx2 = line.indexOf(START_SIG)) >= 0);
    if ( !has_starter || !has_terminator ) {
    if ( !has_starter && !has_terminator ) {
    collect_buffer += line + "\n";
    continue;
    }



    if ( has_starter ) {
    // console.log('has_starter', line_queue);

    if ( !has_terminator || idx2 < idx1 ) {
    console.error(` ${line_count}:\n A paired //@endexport token must be presented before //@export token!`);
    process.exit(1);
    @@ -375,7 +385,7 @@
    line_queue.push(line.substring(idx1+END_SIG_LEN));
    line_count--;
    }


    collect_buffer += line.substring(0, idx1);
    if ( name_key !== null ) {
    @@ -391,6 +401,7 @@
    collect_mode = false;
    collect_buffer = '';
    }

    }
    }
    })()
  2. Cheny-chen revised this gist Feb 24, 2020. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion embedded-script-packer.js
    Original file line number Diff line number Diff line change
    @@ -358,7 +358,7 @@
    // Search for paired //@endexport
    const has_terminator = ((idx1 = line.indexOf(END_SIG)) >= 0);
    const has_starter = ((idx2 = line.indexOf(START_SIG)) >= 0);
    if ( !has_terminator || !has_terminator ) {
    if ( !has_starter || !has_terminator ) {
    collect_buffer += line + "\n";
    continue;
    }
  3. Cheny-chen revised this gist Feb 24, 2020. 1 changed file with 5 additions and 4 deletions.
    9 changes: 5 additions & 4 deletions embedded-script-packer.js
    Original file line number Diff line number Diff line change
    @@ -262,9 +262,9 @@
    function CloseStream(stream){
    return new Promise((resolve, reject)=>stream.end(resolve));
    }
    function ReadMainFolder(folder, arrayFiles = [], arrayFolder = []) {
    fs.readdirSync(folder, {withFileTypes: true}).forEach(filename=>{
    if( filename.isDirectory() ){
    function ReadMainFolder(folder, arrayFiles = [], arrayFolder = []) {
    fs.readdirSync(folder, {withFileTypes: true}).forEach(filename=>{
    if( filename.isDirectory() && filename.name.indexOf('.')!==0 ){
    arrayFolder.push(path.join(folder, filename.name));
    return;
    }
    @@ -281,7 +281,6 @@
    arrayFiles.concat(temp);
    }
    }

    return arrayFiles;
    }
    function GenerateLineReader(filePath) {
    @@ -308,6 +307,8 @@

    const readInterface = GenerateLineReader(filePath);
    for await(const new_line of readInterface) {
    console.log(new_line);

    line_queue.push(new_line);
    ProcessLineQueue();
    }
  4. Cheny-chen revised this gist Feb 11, 2020. 1 changed file with 91 additions and 45 deletions.
    136 changes: 91 additions & 45 deletions embedded-script-packer.js
    Original file line number Diff line number Diff line change
    @@ -17,7 +17,7 @@
    const readline = require('readline');


    const _env_conf = {source_dirs: [], tmpl: null, output: null};
    const _env_conf = {source_dirs: [], skipped_paths:[], tmpl: null, output: null};
    const working_root = process.cwd();


    @@ -58,6 +58,15 @@
    break;
    }

    case '--skip': {
    const tpath = exec_args.pop() || '';
    if ( tpath ) {
    _env_conf.skipped_paths.push(path.resolve(working_root, tpath));
    }
    break;
    }


    case "--output":
    case "-o":{
    const fpath = exec_args.pop() || '';
    @@ -80,7 +89,7 @@


    // Expose collected arguments
    const {tmpl, source_dirs, output} = _env_conf;
    const {tmpl, source_dirs, output, skipped_paths} = _env_conf;


    // Fetch template
    @@ -163,46 +172,62 @@

    // 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 ( const dir_path of source_dirs ){
    const arrayFiles = ReadMainFolder(dir_path);

    for await ( const file of arrayFiles ){
    await ParseFile(file, named_map, unnamed_pool);
    for ( const file of arrayFiles ) {
    if ( skipped_paths.indexOf(file) >= 0 ) continue;

    console.error(`Parsing ${file}...`);
    await ParseFile(file, named_map, unnamed_pool);
    }
    }



    console.error(`\nRendering...`);
    // 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" ){
    if( typeof dynamic_content !== "string" ) {
    await WriteToStream(write_stream, '' + dynamic_content);
    await WriteToStream(write_stream, statics[i]);
    continue;
    }


    if ( dynamic_content === "@unnamed" ) {
    for( const code_segment of unnamed_pool ){
    await WriteToStream(write_stream, code_segment);
    }

    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);
    const seg_pool = named_map[key];
    if( seg_pool !== undefined ){
    for( const code_segment of seg_pool ) {
    await WriteToStream(write_stream, code_segment);
    }
    }
    else if( important ){
    console.error(`Missing required named block \`${ key }\``);
    console.error(` Missing required named block \`${ key }\``);
    process.exit(1);
    }
    else{
    if( !optional ){
    console.error(`Missm matching named block \`${ key }\``);
    console.error(` Miss matching named block \`${ key }\``);
    }

    await WriteToStream(write_stream, '');
    @@ -211,10 +236,6 @@
    await WriteToStream(write_stream, statics[i]);
    }

    for( const code_segment of unnamed_pool ){
    await WriteToStream(write_stream, code_segment);
    }



    await CloseStream(write_stream);
    @@ -241,53 +262,72 @@
    function CloseStream(stream){
    return new Promise((resolve, reject)=>stream.end(resolve));
    }
    async function ReadMainFolder(folder, arrayFiles = [], arrayFolder = []){
    function ReadMainFolder(folder, arrayFiles = [], arrayFolder = []) {
    fs.readdirSync(folder, {withFileTypes: true}).forEach(filename=>{

    if( filename.isDirectory() && !/^\..*|test*/.test(filename.name) ){
    if( filename.isDirectory() ){
    arrayFolder.push(path.join(folder, filename.name));
    return;
    }

    if( !filename.isDirectory() && filename.name.indexOf('.js') > 0 ){
    if ( 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);
    for (const foldername of arrayFolder) {
    if ( skipped_paths.indexOf(foldername) >= 0 ) continue;
    const temp = ReadMainFolder(foldername, arrayFiles);
    arrayFiles.concat(temp);
    }
    }

    return arrayFiles
    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);
    })
    function GenerateLineReader(filePath) {
    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);
    });

    return output;
    }
    async function ParseFile(filePath, named, unnamed){
    const readInterface = await ReadLines(filePath);
    let idx1, idx2;
    let name_key=null;
    let collect_mode=false;
    let collect_buffer='';
    let line_count=0;
    let line_queue=[];



    let idx1, idx2, name_key=null, collect_mode=false, collect_buffer='', line_count=0, line_queue=[];
    const readInterface = GenerateLineReader(filePath);
    for await(const new_line of readInterface) {
    line_queue.push(new_line);
    ProcessLineQueue();
    }

    // Do remaining cleanup on boundary conditions
    if ( collect_mode && collect_buffer !== '' ) {
    console.error(` Unterminated region detected!`);



    if ( name_key !== null ) {
    named[name_key] = named[name_key] || [];
    named[name_key].push(collect_buffer);
    }
    else {
    unnamed.push(collect_buffer);
    }
    }



    function ProcessLineQueue() {
    while(line_queue.length > 0) {
    let line = line_queue.shift();
    line_count++;
    @@ -301,6 +341,11 @@
    collect_mode = true;
    if ( line[idx1+START_SIG_LEN] === '=' ) {
    name_key = line.substring(idx1+START_SIG_LEN+1).trim();

    // Purge key identifiers
    if (name_key[0] === '!' || name_key[0] === '@' || name_key === '?') {
    name_key = name_key.substring(1);
    }
    }

    // Ignore the whole line since that contents before //@export is defined to be discarded
    @@ -320,7 +365,7 @@

    if ( has_starter ) {
    if ( !has_terminator || idx2 < idx1 ) {
    console.error(`${filePath}:${line_count}:\n A paired //@endexport token must be presented before //@export token!`);
    console.error(` ${line_count}:\n A paired //@endexport token must be presented before //@export token!`);
    process.exit(1);
    return;
    }
    @@ -333,7 +378,8 @@

    collect_buffer += line.substring(0, idx1);
    if ( name_key !== null ) {
    named[name_key] = collect_buffer;
    named[name_key] = named[name_key] || [];
    named[name_key].push(collect_buffer);
    }
    else {
    unnamed.push(collect_buffer);
  5. Cheny-chen revised this gist Jan 29, 2020. No changes.
  6. Cheny-chen revised this gist Jan 29, 2020. 1 changed file with 6 additions and 1 deletion.
    7 changes: 6 additions & 1 deletion embedded-script-packer.js
    Original file line number Diff line number Diff line change
    @@ -281,6 +281,7 @@
    const readInterface = await ReadLines(filePath);



    let idx1, idx2, name_key=null, collect_mode=false, collect_buffer='', line_count=0, line_queue=[];
    for await(const new_line of readInterface) {
    line_queue.push(new_line);
    @@ -331,13 +332,17 @@


    collect_buffer += line.substring(0, idx1);
    collect_mode = false;
    if ( name_key !== null ) {
    named[name_key] = collect_buffer;
    }
    else {
    unnamed.push(collect_buffer);
    }


    // Clear state
    collect_mode = false;
    collect_buffer = '';
    }
    }
    }
  7. Cheny-chen revised this gist Jan 29, 2020. 1 changed file with 61 additions and 39 deletions.
    100 changes: 61 additions & 39 deletions embedded-script-packer.js
    Original file line number Diff line number Diff line change
    @@ -5,6 +5,12 @@
    (async()=>{
    "use strict";

    const START_SIG = "//@export";
    const START_SIG_LEN = START_SIG.length;
    const END_SIG = "//@endexport";
    const END_SIG_LEN = END_SIG.length;


    const fs = require('fs');
    const path = require('path');
    const stream = require('stream');
    @@ -155,7 +161,6 @@




    // Collect and parse all js script of the target folders
    const named_map = {}, unnamed_pool = [];
    for await( const dir_path of source_dirs ){
    @@ -166,9 +171,7 @@
    }
    }

    console.log(named_map, unnamed_pool);




    // Output generated script base on template script
    {
    @@ -219,9 +222,6 @@






    function TemplateResolver(strings, ...dynamics) {
    if ( !(this instanceof TemplateResolver) ) {
    return new TemplateResolver(strings, ...dynamics);
    @@ -278,46 +278,68 @@
    })
    }
    async function ParseFile(filePath, named, unnamed){
    return new Promise( async(resolve, reject) => {
    const readInterface = await ReadLines(filePath);


    let idx1, idx2, name_key=null, collect_mode=false, collect_buffer='', line_count=0, line_queue=[];
    for await(const new_line of readInterface) {
    line_queue.push(new_line);

    const readInterface = await ReadLines(filePath);

    let flag = false, splitByEqualsSign = [], temp_unamed = '', index=0;
    for await(let line of readInterface) {

    while(line_queue.length > 0) {
    let line = line_queue.shift();
    line_count++;

    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';
    // Search for //@export
    if ( !collect_mode ) {
    if ( (idx1 = line.indexOf(START_SIG)) < 0 ) continue;

    if( splitByEqualsSign[1] && !named.hasOwnProperty(splitByEqualsSign[1]) ){
    named[splitByEqualsSign[1]] = '';
    }
    else if( splitByEqualsSign.length > 1 && splitByEqualsSign[1] ){
    named[splitByEqualsSign[1]] += line;
    // Parse //@export=[key]
    collect_mode = true;
    if ( line[idx1+START_SIG_LEN] === '=' ) {
    name_key = line.substring(idx1+START_SIG_LEN+1).trim();
    }
    else if( line !== '\n' ){
    temp_unamed += line;

    // Ignore the whole line since that contents before //@export is defined to be discarded
    continue;
    }



    // Search for paired //@endexport
    const has_terminator = ((idx1 = line.indexOf(END_SIG)) >= 0);
    const has_starter = ((idx2 = line.indexOf(START_SIG)) >= 0);
    if ( !has_terminator || !has_terminator ) {
    collect_buffer += line + "\n";
    continue;
    }


    if ( has_starter ) {
    if ( !has_terminator || idx2 < idx1 ) {
    console.error(`${filePath}:${line_count}:\n A paired //@endexport token must be presented before //@export token!`);
    process.exit(1);
    return;
    }

    // The token //@endexport is presented at right position!
    line_queue.push(line.substring(idx1+END_SIG_LEN));
    line_count--;
    }


    collect_buffer += line.substring(0, idx1);
    collect_mode = false;
    if ( name_key !== null ) {
    named[name_key] = collect_buffer;
    }
    else {
    unnamed.push(collect_buffer);
    }
    }

    resolve(true);

    })

    }
    }
    })()
    .catch((e)=>setTimeout(()=>{throw e;}));
    .catch((e)=>setTimeout(()=>{throw e;}));
  8. Cheny-chen revised this gist Jan 29, 2020. 1 changed file with 307 additions and 112 deletions.
    419 changes: 307 additions & 112 deletions embedded-script-packer.js
    Original file line number Diff line number Diff line change
    @@ -1,128 +1,323 @@
    (()=>{
    // NOTE: Helper functions
    const writable=true, configurable=true, enumerable=false;
    const [ExtES, IsNodeJS, Padding, UTF8Encode, UTF8Decode] = (()=>{
    const IsNodeJS = (typeof Buffer !== "undefined" && ArrayBuffer.isView(Buffer));
    const UTF8_DECODE_CHUNK_SIZE = 100;
    const GLOBAL = IsNodeJS ? global : window;
    const ExtES = GLOBAL.ExtES = Object.create(null);



    return [
    ExtES,
    IsNodeJS,
    function Padding(val, length=2, stuffing='0'){
    val = `${val}`;
    let remain = length - val.length;
    while( remain-- > 0 ) {
    val = stuffing + val;
    /**
    * 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;
    }
    return val;
    },
    function UTF8Encode(js_str) {
    if ( typeof js_str !== "string" ) {
    throw new TypeError( "Given input argument must be a js string!" );

    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;
    }

    let codePoints = [];
    let i=0;
    while( i < js_str.length ) {
    let codePoint = js_str.codePointAt(i);

    // 1-byte sequence
    if( (codePoint & 0xffffff80) === 0 ) {
    codePoints.push(codePoint);
    }
    // 2-byte sequence
    else if( (codePoint & 0xfffff800) === 0 ) {
    codePoints.push(
    0xc0 | (0x1f & (codePoint >> 6)),
    0x80 | (0x3f & codePoint)
    );
    }
    // 3-byte sequence
    else if( (codePoint & 0xffff0000) === 0 ) {
    codePoints.push(
    0xe0 | (0x0f & (codePoint >> 12)),
    0x80 | (0x3f & (codePoint >> 6)),
    0x80 | (0x3f & codePoint)
    );
    }
    // 4-byte sequence
    else if( (codePoint & 0xffe00000) === 0 ) {
    codePoints.push(
    0xf0 | (0x07 & (codePoint >> 18)),
    0x80 | (0x3f & (codePoint >> 12)),
    0x80 | (0x3f & (codePoint >> 6)),
    0x80 | (0x3f & codePoint)
    );
    }

    i += (codePoint>0xFFFF) ? 2 : 1;
    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;
    }
    return new Uint8Array(codePoints);
    },
    function UTF8Decode(raw_bytes) {
    if ( raw_bytes instanceof ArrayBuffer ) {
    raw_bytes = new Uint8Array(raw_bytes);
    } 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;
    }


    if ( !(raw_bytes instanceof Uint8Array) ) {
    throw new TypeError( "Given input must be an Uint8Array contains UTF8 encoded value!" );

    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));
    }

    let uint8 = raw_bytes;
    let codePoints = [];
    let i = 0;
    while( i < uint8.length ) {
    let codePoint = uint8[i] & 0xff;
    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';

    // 1-byte sequence (0 ~ 127)
    if( (codePoint & 0x80) === 0 ){
    codePoints.push(codePoint);
    i += 1;
    }
    // 2-byte sequence (192 ~ 223)
    else if( (codePoint & 0xE0) === 0xC0 ){
    codePoint = ((0x1f & uint8[i]) << 6) | (0x3f & uint8[i + 1]);
    codePoints.push(codePoint);
    i += 2;
    }
    // 3-byte sequence (224 ~ 239)
    else if( (codePoint & 0xf0) === 0xe0 ){
    codePoint = ((0x0f & uint8[i]) << 12)
    | ((0x3f & uint8[i + 1]) << 6)
    | (0x3f & uint8[i + 2]);
    codePoints.push(codePoint);
    i += 3;
    if( splitByEqualsSign[1] && !named.hasOwnProperty(splitByEqualsSign[1]) ){
    named[splitByEqualsSign[1]] = '';
    }
    // 4-byte sequence (249 ~ )
    else if( (codePoint & 0xF8) === 0xF0 ){
    codePoint = ((0x07 & uint8[i]) << 18)
    | ((0x3f & uint8[i + 1]) << 12)
    | ((0x3f & uint8[i + 2]) << 6)
    | (0x3f & uint8[i + 3]);
    codePoints.push(codePoint);
    i += 4;
    else if( splitByEqualsSign.length > 1 && splitByEqualsSign[1] ){
    named[splitByEqualsSign[1]] += line;
    }
    else {
    i += 1;
    else if( line !== '\n' ){
    temp_unamed += line;
    }
    }



    let result_string = "";
    while(codePoints.length > 0) {
    const chunk = codePoints.splice(0, UTF8_DECODE_CHUNK_SIZE);
    result_string += String.fromCodePoint(...chunk);
    }
    return result_string;
    }
    ]
    })();

    })();
    resolve(true);

    })

    console.log('ABC-DEF-GGG'.camelCase);
    }
    })()
    .catch((e)=>setTimeout(()=>{throw e;}));
  9. Cheny-chen revised this gist Jan 29, 2020. 1 changed file with 112 additions and 308 deletions.
    420 changes: 112 additions & 308 deletions embedded-script-packer.js
    Original file line number Diff line number Diff line change
    @@ -1,324 +1,128 @@
    /**
    * Author: ChenyChen, JCloudYu
    * Create: 2020/01/29
    **/
    (async()=>{
    "use strict";

    const os = require('os');
    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;
    (()=>{
    // NOTE: Helper functions
    const writable=true, configurable=true, enumerable=false;
    const [ExtES, IsNodeJS, Padding, UTF8Encode, UTF8Decode] = (()=>{
    const IsNodeJS = (typeof Buffer !== "undefined" && ArrayBuffer.isView(Buffer));
    const UTF8_DECODE_CHUNK_SIZE = 100;
    const GLOBAL = IsNodeJS ? global : window;
    const ExtES = GLOBAL.ExtES = Object.create(null);



    return [
    ExtES,
    IsNodeJS,
    function Padding(val, length=2, stuffing='0'){
    val = `${val}`;
    let remain = length - val.length;
    while( remain-- > 0 ) {
    val = stuffing + val;
    }

    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;
    return val;
    },
    function UTF8Encode(js_str) {
    if ( typeof js_str !== "string" ) {
    throw new TypeError( "Given input argument must be a js string!" );
    }
    }
    }
    }



    // 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;
    let codePoints = [];
    let i=0;
    while( i < js_str.length ) {
    let codePoint = js_str.codePointAt(i);

    // 1-byte sequence
    if( (codePoint & 0xffffff80) === 0 ) {
    codePoints.push(codePoint);
    }
    // 2-byte sequence
    else if( (codePoint & 0xfffff800) === 0 ) {
    codePoints.push(
    0xc0 | (0x1f & (codePoint >> 6)),
    0x80 | (0x3f & codePoint)
    );
    }
    // 3-byte sequence
    else if( (codePoint & 0xffff0000) === 0 ) {
    codePoints.push(
    0xe0 | (0x0f & (codePoint >> 12)),
    0x80 | (0x3f & (codePoint >> 6)),
    0x80 | (0x3f & codePoint)
    );
    }
    // 4-byte sequence
    else if( (codePoint & 0xffe00000) === 0 ) {
    codePoints.push(
    0xf0 | (0x07 & (codePoint >> 18)),
    0x80 | (0x3f & (codePoint >> 12)),
    0x80 | (0x3f & (codePoint >> 6)),
    0x80 | (0x3f & codePoint)
    );
    }

    i += (codePoint>0xFFFF) ? 2 : 1;
    }
    } catch(e){
    if( e.code === "ENOENT" ){
    console.error(`Source path \`${ dir_path }\` is not a directory!`);
    process.exit(1);
    return;
    return new Uint8Array(codePoints);
    },
    function UTF8Decode(raw_bytes) {
    if ( raw_bytes instanceof ArrayBuffer ) {
    raw_bytes = new Uint8Array(raw_bytes);
    }

    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 }\``);
    if ( !(raw_bytes instanceof Uint8Array) ) {
    throw new TypeError( "Given input must be an Uint8Array contains UTF8 encoded value!" );
    }

    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 = os.EOL;
    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 + os.EOL;
    let uint8 = raw_bytes;
    let codePoints = [];
    let i = 0;
    while( i < uint8.length ) {
    let codePoint = uint8[i] & 0xff;

    if( splitByEqualsSign[1] && !named.hasOwnProperty(splitByEqualsSign[1]) ){
    named[splitByEqualsSign[1]] = '';
    // 1-byte sequence (0 ~ 127)
    if( (codePoint & 0x80) === 0 ){
    codePoints.push(codePoint);
    i += 1;
    }
    // 2-byte sequence (192 ~ 223)
    else if( (codePoint & 0xE0) === 0xC0 ){
    codePoint = ((0x1f & uint8[i]) << 6) | (0x3f & uint8[i + 1]);
    codePoints.push(codePoint);
    i += 2;
    }
    // 3-byte sequence (224 ~ 239)
    else if( (codePoint & 0xf0) === 0xe0 ){
    codePoint = ((0x0f & uint8[i]) << 12)
    | ((0x3f & uint8[i + 1]) << 6)
    | (0x3f & uint8[i + 2]);
    codePoints.push(codePoint);
    i += 3;
    }
    else if( splitByEqualsSign.length > 1 && splitByEqualsSign[1] ){
    named[splitByEqualsSign[1]] += line;
    // 4-byte sequence (249 ~ )
    else if( (codePoint & 0xF8) === 0xF0 ){
    codePoint = ((0x07 & uint8[i]) << 18)
    | ((0x3f & uint8[i + 1]) << 12)
    | ((0x3f & uint8[i + 2]) << 6)
    | (0x3f & uint8[i + 3]);
    codePoints.push(codePoint);
    i += 4;
    }
    else if( line !== os.EOL ){
    temp_unamed += line;
    else {
    i += 1;
    }
    }



    let result_string = "";
    while(codePoints.length > 0) {
    const chunk = codePoints.splice(0, UTF8_DECODE_CHUNK_SIZE);
    result_string += String.fromCodePoint(...chunk);
    }
    return result_string;
    }
    ]
    })();

    resolve(true);

    })
    })();

    }
    })()
    .catch((e)=>setTimeout(()=>{throw e;}));
    console.log('ABC-DEF-GGG'.camelCase);
  10. Cheny-chen revised this gist Jan 29, 2020. 1 changed file with 272 additions and 123 deletions.
    395 changes: 272 additions & 123 deletions embedded-script-packer.js
    Original file line number Diff line number Diff line change
    @@ -1,112 +1,247 @@
    /**
    * Author: ChenyChen, JCloudYu
    * Create: 2020/01/29
    **/
    const os = require('os');
    const fs = require('fs');
    const path = require('path');
    const readline = require('readline');



    **/
    (async()=>{
    'use strict';

    if( process.argv.length < 3 ){
    return console.error('** Missing Directory and Destination Filename!');
    }

    const named_map = {}, unnamed_pool = [];
    const arrayFiles = await ReadMainFolder(process.argv[2]);
    for( const file of arrayFiles ){
    ParseFile(file, named_map, unnamed_pool);
    }
    "use strict";

    const os = require('os');
    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();


    const output_map = [
    { template:"./tmpl1.js", output:"./output.js" }
    ];
    const TemplateResolver = global.BuildTemplate = function(strings, ...dynamics) {
    if ( this instanceof TemplateResolver) {
    this.statics = strings;
    this.dynamics = dynamics;
    // 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));

    return new TemplateResolver(strings, ...dynamics);
    };
    for(const {template:_tmpl_name, output:_out_name} of output_map) {
    try {
    const out_file = path.resolve(__dirname, _out_name);
    const write_stream = fs.createWriteStream(out_file, {flags: 'w'});

    const tmpl_name = path.resolve(__dirname, _tmpl_name);
    const tmpl = require(tmpl_name);

    if ( tmpl instanceof TemplateResolver ) {
    const {statics, dynamics} = tmpl;

    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 ) {
    throw new Error(`Missing required named block \`${key}\``);

    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));
    }
    else {
    if ( !optional ) {
    console.error(`Mismatched named block \`${key}\``);
    }

    await WriteToStream(write_stream, '');
    break;
    }

    case "--output":
    case "-o":{
    const fpath = exec_args.pop() || '';
    if( fpath ){
    _env_conf.output = path.resolve(working_root, fpath);
    }

    await WriteToStream(write_stream, statics[i]);
    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;
    }

    for ( const code_segment of unnamed_pool ) {
    await WriteToStream(write_stream, code_segment);
    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;
    }


    await CloseStream(write_stream);

    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;
    }
    }
    catch(e) {
    console.error(e);
    }



    // 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;
    }
    function WriteToStream(stream, data) {




    // 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 ) {
    if( !should_wait ){
    reject(false);
    }
    });
    }
    function CloseStream(stream) {
    function CloseStream(stream){
    return new Promise((resolve, reject)=>stream.end(resolve));
    }
    }
    async function ReadMainFolder(folder, arrayFiles = [], arrayFolder = []){
    fs.readdirSync(folder, {withFileTypes: true}).forEach(filename=>{

    @@ -120,56 +255,70 @@ const readline = require('readline');
    });

    if( arrayFolder.length > 0 ){
    const temp = await ReadMainFolder(arrayFolder[0], arrayFiles);
    arrayFiles.concat(temp);
    for await(const foldername of arrayFolder) {
    const temp = await ReadMainFolder(foldername, arrayFiles);
    arrayFiles.concat(temp);
    }
    }

    console.log('** The List of Files');
    console.log(arrayFiles);
    return arrayFiles
    }
    function ParseFile(filePath, named, unnamed){
    const readInterface = readline.createInterface({
    input: fs.createReadStream(filePath),
    });


    readInterface.on('line', generateContain);

    let flag = false, splitByEqualsSign = [], temp_unamed = '';
    function generateContain(line){

    if( line.indexOf('//@export') > -1 ){
    splitByEqualsSign = line.split('=');
    line = os.EOL;
    flag = true;
    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 = os.EOL;
    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 + os.EOL;

    if( splitByEqualsSign[1] && !named.hasOwnProperty(splitByEqualsSign[1]) ){
    named[splitByEqualsSign[1]] = '';
    }
    else if( splitByEqualsSign.length > 1 && splitByEqualsSign[1] ){
    named[splitByEqualsSign[1]] += line;
    }
    else if( line !== os.EOL ){
    temp_unamed += line;
    }
    }
    }
    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.trim() + os.EOL;

    if( splitByEqualsSign[1] && !named.hasOwnProperty(splitByEqualsSign[1]) ){
    named[splitByEqualsSign[1]] = '';
    }
    else if( splitByEqualsSign.length > 1 && splitByEqualsSign[1] ){
    named[splitByEqualsSign[1]] += line;
    }
    else if( line != os.EOL ){
    temp_unamed += line;
    }
    }
    resolve(true);

    })

    }

    }

    })();
    }
    })()
    .catch((e)=>setTimeout(()=>{throw e;}));
  11. Cheny-chen revised this gist Jan 29, 2020. 1 changed file with 166 additions and 62 deletions.
    228 changes: 166 additions & 62 deletions embedded-script-packer.js
    Original file line number Diff line number Diff line change
    @@ -1,71 +1,175 @@
    (async () => {
    'use stric';
    const os = require('os');
    const fs = require('fs');
    const path = require('path');
    const readline = require('readline');
    /**
    * Author: ChenyChen, JCloudYu
    * Create: 2020/01/29
    **/
    const os = require('os');
    const fs = require('fs');
    const path = require('path');
    const readline = require('readline');

    if (process.argv.length < 3) {
    return console.error('** Missing Director and Destination Filename!');
    }
    else if (process.argv.length < 4) {
    return console.error('** Missing Destination Filename!');
    }

    const writeFile = fs.createWriteStream( process.argv[3], {flags: 'a'} );

    async function ReadMainFoler(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) {
    const temp = await ReadMainFoler(arrayFolder[0], arrayFiles);
    arrayFiles.concat(temp);
    }

    console.log('** The List of Files'.blue);
    console.log(arrayFiles);
    return arrayFiles
    }
    (async()=>{
    'use strict';

    if( process.argv.length < 3 ){
    return console.error('** Missing Directory and Destination Filename!');
    }

    const named_map = {}, unnamed_pool = [];
    const arrayFiles = await ReadMainFolder(process.argv[2]);
    for( const file of arrayFiles ){
    ParseFile(file, named_map, unnamed_pool);
    }





    const output_map = [
    { template:"./tmpl1.js", output:"./output.js" }
    ];
    const TemplateResolver = global.BuildTemplate = function(strings, ...dynamics) {
    if ( this instanceof TemplateResolver) {
    this.statics = strings;
    this.dynamics = dynamics;
    return;
    }

    return new TemplateResolver(strings, ...dynamics);
    };
    for(const {template:_tmpl_name, output:_out_name} of output_map) {
    try {
    const out_file = path.resolve(__dirname, _out_name);
    const write_stream = fs.createWriteStream(out_file, {flags: 'w'});

    const tmpl_name = path.resolve(__dirname, _tmpl_name);
    const tmpl = require(tmpl_name);

    if ( tmpl instanceof TemplateResolver ) {
    const {statics, dynamics} = tmpl;

    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 ) {
    throw new Error(`Missing required named block \`${key}\``);
    }
    else {
    if ( !optional ) {
    console.error(`Mismatched 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);
    }
    catch(e) {
    console.error(e);
    }
    }
    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 ){
    const temp = await ReadMainFolder(arrayFolder[0], arrayFiles);
    arrayFiles.concat(temp);
    }

    console.log('** The List of Files');
    console.log(arrayFiles);
    return arrayFiles
    }
    function ParseFile(filePath, named, unnamed){
    const readInterface = readline.createInterface({
    input: fs.createReadStream(filePath),
    });


    readInterface.on('line', generateContain);

    let flag = false, splitByEqualsSign = [], temp_unamed = '';
    function generateContain(line){

    function AppendFiles(filePath) {
    const readInterface = readline.createInterface({
    input: fs.createReadStream(filePath),
    });

    readInterface.on('line', replaceWords);

    let flag = false;
    function replaceWords(line) {

    if (line.indexOf('//@export=') > -1) {
    flag = true;
    if( line.indexOf('//@export') > -1 ){
    splitByEqualsSign = line.split('=');
    line = os.EOL;
    }
    else if (line.indexOf('//@endexport') > -1) {
    line = line.replace('//@endexport','');
    writeFile.write(line + os.EOL);
    flag = true;
    }
    else if( line.indexOf('//@endexport') > -1 ){
    line = line.replace('//@endexport', '');
    flag = false;
    }
    if(flag) {
    writeFile.write(line + os.EOL);
    }
    if(temp_unamed){
    unnamed.push(temp_unamed);
    temp_unamed = '';
    }

    }

    }
    }

    const arrayFiles = await ReadMainFoler( process.argv[2] );
    if( flag ){
    line = line.trim() + os.EOL;

    if( splitByEqualsSign[1] && !named.hasOwnProperty(splitByEqualsSign[1]) ){
    named[splitByEqualsSign[1]] = '';
    }
    else if( splitByEqualsSign.length > 1 && splitByEqualsSign[1] ){
    named[splitByEqualsSign[1]] += line;
    }
    else if( line != os.EOL ){
    temp_unamed += line;
    }
    }

    for (const file of arrayFiles) {
    AppendFiles(file)
    }

    }

    })()
    })();
  12. Cheny-chen revised this gist Jan 28, 2020. 1 changed file with 2 additions and 3 deletions.
    5 changes: 2 additions & 3 deletions embedded-script-packer.js
    Original file line number Diff line number Diff line change
    @@ -3,14 +3,13 @@
    const os = require('os');
    const fs = require('fs');
    const path = require('path');
    const colors = require('colors');
    const readline = require('readline');

    if (process.argv.length < 3) {
    return console.error('** Missing Director and Destination Filename!'.red);
    return console.error('** Missing Director and Destination Filename!');
    }
    else if (process.argv.length < 4) {
    return console.error('** Missing Destination Filename!'.red);
    return console.error('** Missing Destination Filename!');
    }

    const writeFile = fs.createWriteStream( process.argv[3], {flags: 'a'} );
  13. Cheny-chen created this gist Jan 28, 2020.
    72 changes: 72 additions & 0 deletions embedded-script-packer.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,72 @@
    (async () => {
    'use stric';
    const os = require('os');
    const fs = require('fs');
    const path = require('path');
    const colors = require('colors');
    const readline = require('readline');

    if (process.argv.length < 3) {
    return console.error('** Missing Director and Destination Filename!'.red);
    }
    else if (process.argv.length < 4) {
    return console.error('** Missing Destination Filename!'.red);
    }

    const writeFile = fs.createWriteStream( process.argv[3], {flags: 'a'} );

    async function ReadMainFoler(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) {
    const temp = await ReadMainFoler(arrayFolder[0], arrayFiles);
    arrayFiles.concat(temp);
    }

    console.log('** The List of Files'.blue);
    console.log(arrayFiles);
    return arrayFiles
    }

    function AppendFiles(filePath) {
    const readInterface = readline.createInterface({
    input: fs.createReadStream(filePath),
    });

    readInterface.on('line', replaceWords);

    let flag = false;
    function replaceWords(line) {

    if (line.indexOf('//@export=') > -1) {
    flag = true;
    line = os.EOL;
    }
    else if (line.indexOf('//@endexport') > -1) {
    line = line.replace('//@endexport','');
    writeFile.write(line + os.EOL);
    flag = false;
    }
    if(flag) {
    writeFile.write(line + os.EOL);
    }

    }
    }

    const arrayFiles = await ReadMainFoler( process.argv[2] );

    for (const file of arrayFiles) {
    AppendFiles(file)
    }

    })()