ES6

ES6

ECMAScript 6 (ES6) is the upcoming sixth major release of the ECMAScript language specification.
Lawrence  Lesch

Lawrence Lesch

1662107520

Superdom: Better and Simpler ES6 DOM Manipulation

Superdom

You have dom. It has all the DOM virtually within it. Use that power:

// Fetch all the page links
let links = dom.a.href;

// Links open in a new tab
dom.a.target = '_blank';

Only for modern browsers

Getting started

Simply use the CDN via unpkg.com:

<script src="https://unpkg.com/superdom@1"></script>

Or use npm or bower:

npm|bower install superdom --save

Select

It always returns an array with the matched elements. Get all the elements that match the selector:

// Simple element selector into an array
let allLinks = dom.a;

// Loop straight on the selection
dom.a.forEach(link => { ... });

// Combined selector
let importantLinks = dom['a.important'];

There are also some predetermined elements, such as id, class and attr:

// Select HTML Elements by id:
let main = dom.id.main;

// by class:
let buttons = dom.class.button;

// or by attribute:
let targeted = dom.attr.target;
let targeted = dom.attr['target="_blank"'];

Generate

Use it as a function or a tagged template literal to generate DOM fragments:

// Not a typo; tagged template literals
let link = dom`<a href="https://google.com/">Google</a>`;

// It is the same as
let link = dom('<a href="https://google.com/">Google</a>');

Delete elements

Delete a piece of the DOM

// Delete all of the elements with the class .google
delete dom.class.google;   // Is this an ad-block rule?

Attributes

You can easily manipulate attributes right from the dom node. There are some aliases that share the syntax of the attributes such as html and text (aliases for innerHTML and textContent). There are others that travel through the dom such as parent (alias for parentNode) and children. Finally, class behaves differently as explained below.

Get attributes

The fetching will always return an array with the element for each of the matched nodes (or undefined if not there):

// Retrieve all the urls from the page
let urls = dom.a.href;     // #attr-list
  // ['https://google.com', 'https://facebook.com/', ...]

// Get an array of the h2 contents (alias of innerHTML)
let h2s = dom.h2.html;     // #attr-alias
  // ['Level 2 header', 'Another level 2 header', ...]

// Get whether any of the attributes has the value "_blank"
let hasBlank = dom.class.cta.target._blank;    // #attr-value
  // true/false

You also use these:

  • html (alias of innerHTML): retrieve a list of the htmls
  • text (alias of textContent): retrieve a list of the htmls
  • parent (alias of parentNode): travel up one level
  • children: travel down one level

Set attributes

// Set target="_blank" to all links
dom.a.target = '_blank';     // #attr-set
dom.class.tableofcontents.html = `
  <ul class="tableofcontents">
    ${dom.h2.map(h2 => `
      <li>
        <a href="#${h2.id}">
          ${h2.innerHTML}
        </a>
      </li>
    `).join('')}
  </ul>
`;

Remove an attribute

To delete an attribute use the delete keyword:

// Remove all urls from the page
delete dom.a.href;

// Remove all ids
delete dom.a.id;

Classes

It provides an easy way to manipulate the classes.

Get classes

To retrieve whether a particular class is present or not:

// Get an array with true/false for a single class
let isTest = dom.a.class.test;     // #class-one

For a general method to retrieve all classes you can do:

// Get a list of the classes of each matched element
let arrays = dom.a.class;     // #class-arrays
  // [['important'], ['button', 'cta'], ...]

// If you want a plain list with all of the classes:
let flatten = dom.a.class._flat;     // #class-flat
  // ['important', 'button', 'cta', ...]

// And if you just want an string with space-separated classes:
let text = dom.a.class._text;     // #class-text
  // 'important button cta ...'

Add a class

// Add the class 'test' (different ways)
dom.a.class.test = true;    // #class-make-true
dom.a.class = 'test';       // #class-push

Remove a class

// Remove the class 'test'
dom.a.class.test = false;    // #class-make-false

Manipulate

Did we say it returns a simple array?

dom.a.forEach(link => link.innerHTML = 'I am a link');

But what an interesting array it is; indeed we are also proxy'ing it so you can manipulate its sub-elements straight from the selector:

// Replace all of the link's html with 'I am a link'
dom.a.html = 'I am a link';

Of course we might want to manipulate them dynamically depending on the current value. Just pass it a function:

// Append ' ^_^' to all of the links in the page
dom.a.html = html => html + ' ^_^';

// Same as this:
dom.a.forEach(link => link.innerHTML = link.innerHTML + ' ^_^');

Note: this won't work dom.a.html += ' ^_^'; for more than 1 match (for reasons)

Or get into genetics to manipulate the attributes:

dom.a.attr.target = '_blank';

// Only to external sites:
let isOwnPage = el => /^https?\:\/\/mypage\.com/.test(el.getAttribute('href'));
dom.a.attr.target = (prev, i, element) => isOwnPage(element) ? '' : '_blank';

Events

You can also handle and trigger events:

// Handle click events for all <a>
dom.a.on.click = e => ...;

// Trigger click event for all <a>
dom.a.trigger.click;

Testing

We are using Jest as a Grunt task for testing. Install Jest and run in the terminal:

grunt watch

Download Details:

Author: franciscop
Source Code: https://github.com/franciscop/superdom 
License: MIT license

#javascript #es6 #dom 

Superdom: Better and Simpler ES6 DOM Manipulation
Gordon  Taylor

Gordon Taylor

1661955240

Blockbase: Lightweight MVC Framework for Node.js

Blockbase

Lightweight MVC Framework for Node  

Version

v1.0.20

Install

You need first to have Node.JS on your machine. If not please folllow the install instructions here

Then let's move on :

Install Blockbase

$ npm install -g blockbase

Create a project using the CLI

$ blockbase create MySuperProject

Discover the architecture

Blockbase is based on an hybrid MVC+Drivers architecture to build complete projects and scalable architectures.

/config (required, where you'll push your configuration)
/drivers
/controllers
/models
app.js

Edit your app.js Blockbase is using a simple instance method : blockbase(options, callback) In options, the only mandatory property is root handling the path of the current project (see example below). ``` js const blockbase = require('blockbase')

blockbase({ root : __dirname }, async (app) => { app.drivers.logger.success('App', ${app.config.name} is alive !) // let's code ! })


#### Namespace
If you log the `app` variable from the callback, you'll get the following architecture :
* `app.config`: contains the config JSON from /config/{env}.yml (see [config](https://www.npmjs.com/package/config) package)
* `app.root`: path where the app is launched
* `app.drivers`: drivers namespace, by default is containing only `app.drivers.logger` handling the console logs. You can install more drivers (see more [Drivers Install](#drivers))
* `app.controllers`: will be automatically populated by the files from /controllers/* (see more [Managing Controllers](#controllers))
* `app.models`: will be automatically populated by the files from /models/* (see more [Managing Models](#models))

#### Drivers

Blockbase is build with a driver linked logic to build connectors to other tools or customer traversal methods across your app. 
We offer a list of [official drivers here](https://github.com/blacksmithstudio/blockbase/blob/master/docs/DRIVERS.md) to install connectors such as [MySQL](https://github.com/blacksmithstudio/blockbase-mysql), [PostgreSQL](https://github.com/blacksmithstudio/blockbase-postgresql), ... 

##### Automatic install for official drivers.
You can easily install [official drivers](https://github.com/blacksmithstudio/blockbase/blob/master/docs/DRIVERS.md) by using `npm i` in your project. This will automatically add the driver to the blockbase namespace `app.drivers.*`
``` shell
$ npm i --save blockbase-express

In the example above, the driver will be install under the app.drivers.express namespace

Manual Install for your custom drivers.

You can create your own drivers by adding them to the /drivers/ folder using the CLI.

$ blockbase add driver custom

Blockbase structure allows you to pass the entire app.* namespace to your drivers, controllers, etc... Here is an example of a custom driver :

example below : /drivers/custom.js

const something = require('something')

module.exports = (app) => {
    // setup of your driver
    return {
        foo(arg1, arg2) {
            // do something
        },

        bar() {
            // do something
        }
    }
}

Following that you'll be able to use anywere the driver by calling app.drivers.custom.foo(arg1, arg2) for example. !!! Please don't call any controller or model in the driver above the return statement as it is instanciated at the application initialization.

Controllers

Controllers will follow the same rules, you want to create a controller ? Just add it under /controllers, but there are some differences.

  • Controllers could have an optional init method, triggered on the creation of the app.
  • Controllers can have sub namespaces (2 dimensions max) like app.controllers.sub.foo.bar

Example of Architecture :

/config
/drivers
/controllers
---/custom.js
---/foo/bar.js
/models
app.js

Following the construction above, Blockbase will render app.controllers.custom.* and app.controllers.foo.bar.*

To create a controller

$ blockbase add controller foo

To create a sub.controller

$ blockbase add controller foo.bar

Models

Models follow a slight different approach, using class and extends properties of ES6.

Building a custom model from scratch

You can build a custom model with no inherited properties and submethods. Adding it directly to /models/ will add it to the app.models.* namespace

To create a model with the CLI

$ blockbase add model user

Example : /models/user.js

module.exports = (app) => {
    return class User {
        constructor(data){
            // init my model
        }

        example(){
            // model sub method
        }
    }
}

However this model is limited, having only its declared subproperties. Blockbase has by default a serie of classic methods powered in the models (create, read, update, delete, etc.) useful in your API build-up. To activate these methods, use the inheritance below :

Building a custom model with Blockbase inheritance

Example : /models/user.js

module.exports = (app) => {
    // we call the "super model" from the namespace
    const Model = app.models._model
    
    // we extend the super model in our user model so it will receive all the default methods.
    return class User extends Model {
        constructor(data){
            super({ type : 'awesome', authenticated : false })

            if(data)
                this.data = data
        }

        example(){
            // example of an additional method
        }
    }
}

The main change is on the Model inheritance.

    const Model = app.models._model
    [...]
    return class Awesome extends Model {

Thanks to this extend, you'll get access to a lot of default methods in the model.

   const User = app.models.user
   let user = new User({ firstname : 'John', lastname : 'Doe' })
   
   console.log(user.body()) // will show you the data 
   console.log(user.clean()) // will remove null/empty data from the object
   etc...

Default methods in the model

  • {model}.body() allowing you to access the data (if your model has a.dataobject)
  • {model}.clean() remove all the null and empty values from your data
  • {model}.validate() returns the Joi validation of your model
  • {model}.valid() returns a boolean if your object data is Joi validated or not

Default methods in the model (be careful : a DBMS driver is required, for example blockbase-postgresql)

  • await {model}.create() returns a new saved object in your database
  • await {model}.read() returns an object from your database (search by id)
  • await {model}.update() returns an updated object from your database
  • await {model}.delete() returns a boolean on deletion of your object (by id)

Run tests

Blockbase has some unit tests (with Mocha) written run them often !

$ npm test

Download Details:

Author: Blacksmithstudio
Source Code: https://github.com/blacksmithstudio/blockbase 
License: MIT license

#javascript #node #framework #mvc #es6 

Blockbase: Lightweight MVC Framework for Node.js
Reid  Rohan

Reid Rohan

1661788980

JSBarcode: Barcode Generation Library Written in JavaScript

Introduction

JsBarcode is a barcode generator written in JavaScript. It supports multiple barcode formats and works in browsers and with Node.js. It has no dependencies when it is used for the web but works with jQuery if you are into that.

Examples for browsers:

First create a canvas (or image)

<svg id="barcode"></svg>
<!-- or -->
<canvas id="barcode"></canvas>
<!-- or -->
<img id="barcode"/>

Simple example:

JsBarcode("#barcode", "Hi!");
// or with jQuery
$("#barcode").JsBarcode("Hi!");

Result:

Result

Example with options:

JsBarcode("#barcode", "1234", {
  format: "pharmacode",
  lineColor: "#0aa",
  width:4,
  height:40,
  displayValue: false
});

Result:

Result

More advanced use case:

JsBarcode("#barcode")
  .options({font: "OCR-B"}) // Will affect all barcodes
  .EAN13("1234567890128", {fontSize: 18, textMargin: 0})
  .blank(20) // Create space between the barcodes
  .EAN5("12345", {height: 85, textPosition: "top", fontSize: 16, marginTop: 15})
  .render();

Result:

Result

Or define the value and options in the HTML element:

Use any jsbarcode-* or data-* as attributes where * is any option.

<svg class="barcode"
  jsbarcode-format="upc"
  jsbarcode-value="123456789012"
  jsbarcode-textmargin="0"
  jsbarcode-fontoptions="bold">
</svg>

And then initialize it with:

JsBarcode(".barcode").init();

Result:

Result

Retrieve the barcode values so you can render it any way you'd like

Pass in an object which will be filled with data.

const data = {};
JsBarcode(data, 'text', {...options});

data will be filled with a encodings property which has all the needed values. See wiki for an example of what data looks like.

Setup for browsers:

Step 1:

Download or get the CDN link to the script:

NameSupported barcodesSize (gzip)CDN / Download
AllAll the barcodes!10.1 kB[JsBarcode.all.min.js][1]
CODE128CODE128 (auto and force mode)6.2 kB[JsBarcode.code128.min.js][2]
CODE39CODE395.1 kB[JsBarcode.code39.min.js][3]
EAN / UPCEAN-13, EAN-8, EAN-5, EAN-2, UPC (A)6.7 kB[JsBarcode.ean-upc.min.js][4]
ITFITF, ITF-145 kB[JsBarcode.itf.min.js][5]
MSIMSI, MSI10, MSI11, MSI1010, MSI11105 kB[JsBarcode.msi.min.js][6]
PharmacodePharmacode4.7 kB[JsBarcode.pharmacode.min.js][7]
CodabarCodabar4.9 kB[JsBarcode.codabar.min.js][8]

Step 2:

Include the script in your code:

<script src="JsBarcode.all.min.js"></script>

Step 3:

You are done! Go generate some barcodes :smile:

Bower and npm:

You can also use Bower or npm to install and manage the library.

bower install jsbarcode --save
npm install jsbarcode --save

Node.js:

With canvas:

var JsBarcode = require('jsbarcode');

// Canvas v1
var Canvas = require("canvas");
// Canvas v2
var { createCanvas } = require("canvas");

// Canvas v1
var canvas = new Canvas();
// Canvas v2
var canvas = createCanvas();

JsBarcode(canvas, "Hello");

// Do what you want with the canvas
// See https://github.com/Automattic/node-canvas for more information

With svg:

const { DOMImplementation, XMLSerializer } = require('xmldom');
const xmlSerializer = new XMLSerializer();
const document = new DOMImplementation().createDocument('http://www.w3.org/1999/xhtml', 'html', null);
const svgNode = document.createElementNS('http://www.w3.org/2000/svg', 'svg');

JsBarcode(svgNode, 'test', {
    xmlDocument: document,
});

const svgText = xmlSerializer.serializeToString(svgNode);

Options:

For information about how to use the options, see the wiki page.

OptionDefault valueType
format"auto" (CODE128)String
width2Number
height100Number
displayValuetrueBoolean
textundefinedString
fontOptions""String
font"monospace"String
textAlign"center"String
textPosition"bottom"String
textMargin2Number
fontSize20Number
background"#ffffff"String (CSS color)
lineColor"#000000"String (CSS color)
margin10Number
marginTopundefinedNumber
marginBottomundefinedNumber
marginLeftundefinedNumber
marginRightundefinedNumber
validfunction(valid){}Function

Demo

Barcode Generator

Simple CodePen Demo

Settings CodePen Demo

Supported barcodes:


Contributions and feedback:

We ❤️ contributions and feedback.

If you want to contribute, please check out the CONTRIBUTING.md file.

If you have any question or suggestion create an issue or ask about it in the gitter chat.

Bug reports should always be done with a new issue.

Download Details:

Author: lindell
Source Code: https://github.com/lindell/JsBarcode 
License: MIT license

#javascript #barcode #node #es6 

JSBarcode: Barcode Generation Library Written in JavaScript
Gordon  Taylor

Gordon Taylor

1660674600

A Template for Building Cross Browser Extensions For Chrome, Opera

Extension Boilerplate

A foundation for creating browser extensions for Chrome, Opera & Firefox.

Now that Firefox supports WebExtensions, it has become a lot easier to build browser extensions/addons for multiple browsers without duplicating the codebase. This project serves as a sensible starting point to help you get started.

Extension Boilerplate

I have extracted this from the browser extensions that I built for my side-project, Email This.

Side note: Do check out Email This. It is a simpler alternative to bookmarking tools like Pocket, Readability & Instapaper. Email This will remove ads & distractions from an article and send you a nice email with just the text/images. No need to install any additional applications or login to another app just to access your bookmarks. The Chrome Extensions is available on the Chrome Web Store.

Features

Write once and deploy to Chrome, Opera & Firefox

Based on WebExtensions. It also includes a tiny polyfill to bring uniformity to the APIs exposed by different browsers.

Live-reload

Your changes to CSS, HTML & JS files will be relayed instantly without having to manually reload the extension. This ends up saving a lot of time and improving the developer experience.

Sensible starting point

This comes with a gulp based workflow that converts modern ES6 JavaScript and SCSS to JS/CSS. Scripts are transpiled using Babel and bundled using Browserify. Sourcemaps are available for both JS and SCSS.

Sketch (.sketch) assets for icons and promo images

A .sketch file is included in the resources directory. This has all the icons and promo images that will be needed while uploading the extensions to the app stores.

Easily configurable and extendable

The gulpfile is easily understandable and configurable. If you want to add additional tasks or remove un-used ones, you can easily tweak that file to suit your needs.

Platform specific & Environment specific variables.

You might need to specify different data variables based on your environment. For example, you might want to use a localhost API endpoint during development and a production API endpoint once the extension is submitted to the appstore. You can specify such data in the json files inside `config` directory.

You can also set custom data variables based on the platform (different variable for Chrome, FF, Opera).

Installation

  1. Clone the repository git clone https://github.com/EmailThis/extension-boilerplate.git
  2. Run npm install
  3. Run npm run build

Alternately, if you want to try out the sample extension, here are the download links. After you download it, unzip the file and load it in your browser using the steps mentioned below.

Load the extension in Chrome & Opera

  1. Open Chrome/Opera browser and navigate to chrome://extensions
  2. Select "Developer Mode" and then click "Load unpacked extension..."
  3. From the file browser, choose to extension-boilerplate/build/chrome or (extension-boilerplate/build/opera)

Load the extension in Firefox

  1. Open Firefox browser and navigate to about:debugging
  2. Click "Load Temporary Add-on" and from the file browser, choose extension-boilerplate/build/firefox

Developing

The following tasks can be used when you want to start developing the extension and want to enable live reload -

  • npm run chrome-watch
  • npm run opera-watch
  • npm run firefox-watch

Packaging

Run npm run dist to create a zipped, production-ready extension for each browser. You can then upload that to the appstore.

TODO

  •  Add support for Safari
  •  Add Firefox & Opera Promo images
  •  Add sample screenshot templates
  •  Write a guide for using config variables & JS preprocessor

If you have any questions or comments, please create a new issue. I'd be happy to hear your thoughts.

Bharani, Email This

Download Details:

Author: EmailThis
Source Code: https://github.com/EmailThis/c 
License: MIT license

#javascript #gulp #chrome #es6 

A Template for Building Cross Browser Extensions For Chrome, Opera
Garry Taylor

Garry Taylor

1659431073

JavaScript Tutorial: Metaprogramming in JavaScript

In this tutorial, we'll learn about Metaprogramming in JavaScript. ES6 greatly expands upon JavaScript's existing metaprogramming capabilities with the Symbol, Reflect, and Proxy types.

Metadata, data about data, is everywhere. We seem to intrinsically understand that using data to further describe the data within our systems brings numerous benefits to taming complexity. It follows then that metaprogramming, programming that interacts with the program itself by inspecting or even manipulating its own code can bring similar benefits to our software.

ES6 greatly expands upon JavaScript's existing metaprogramming capabilities with the Symbol, Reflect, and Proxy types. Through some practical examples we'll discuss the role each of these types play within JavaScript metaprogramming and see how they not only affect your code but even drive several modern language features.

#javascript #es6

JavaScript Tutorial: Metaprogramming in JavaScript

Modern Javascript Methods (ES6+) for The Earth Engine Code Editor

ee-polyfill

Description

ee-polyfill loads newer JavaScript methods (ES6+) into the Earth Engine Code Editor with a single import. Implementations are originally from behnammodi/polyfill, with modifications to ensure Earth Engine compatibility.

Usage

Just import the module with require to load all of the polyfilled methods.

require("users/aazuspan/polyfill:all");

New methods are automatically added to JavaScript objects. A few examples are demonstrated below:

// Array methods
var a = [0, [1, 1], [[8, 9, 9, 9]]];
a.includes(0); // true
a.flat(2); // [0, 1, 1, 8, 9, 9, 9]

// Math methods
var x = 4.05;
Math.sign(x); // 1
Math.trunc(x); // 4

// Number methods
var n = 32768;
Number.isInteger(n); // true

// Object methods
var o = {"a": 42, "b": 12};
Object.values(o); // [42, 12]
Object.entries(o); // [["a", 42], ["b", 12]]

// String methods
var s = "abc";
s.startsWith("b"); // false
s.repeat(3); // abcabcabc

Methods

ee-polyfill includes all of the methods listed at behnammodi/polyfill, plus those listed below:

Object

Object.values();
Object.entries();
Object.fromEntries();

Author: aazuspan
Source Code: https://github.com/aazuspan/ee-polyfill 
License: MIT license

#javascript #es6 #code #editor 

Modern Javascript Methods (ES6+) for The Earth Engine Code Editor
Gordon  Taylor

Gordon Taylor

1658023440

Source Transformer Enabling ES6 Generator Functions in JS-of-today

regenerator 

This package implements a fully-functional source transformation that takes the syntax for generators/yield from ECMAScript 2015 or ES2015 and Asynchronous Iteration proposal and spits out efficient JS-of-today (ES5) that behaves the same way.

A small runtime library (less than 1KB compressed) is required to provide the wrapGenerator function. You can install it either as a CommonJS module or as a standalone .js file, whichever you prefer.

Installation

From npm:

npm install -g regenerator

From GitHub:

cd path/to/node_modules
git clone git://github.com/facebook/regenerator.git
cd regenerator
npm install .
npm test

Usage

You have several options for using this module.

Simplest usage:

regenerator es6.js > es5.js # Just the transform.
regenerator --include-runtime es6.js > es5.js # Add the runtime too.
regenerator src lib # Transform every .js file in src and output to lib.

Programmatic usage:

var es5Source = require("regenerator").compile(es6Source).code;
var es5SourceWithRuntime = require("regenerator").compile(es6Source, {
  includeRuntime: true
}).code;

AST transformation:

var recast = require("recast");
var ast = recast.parse(es6Source);
ast = require("regenerator").transform(ast);
var es5Source = recast.print(ast);

How can you get involved?

The easiest way to get involved is to look for buggy examples using the sandbox, and when you find something strange just click the "report a bug" link (the new issue form will be populated automatically with the problematic code).

Alternatively, you can fork the repository, create some failing tests cases in test/tests.es6.js, and send pull requests for me to fix.

If you're feeling especially brave, you are more than welcome to dive into the transformer code and fix the bug(s) yourself, but I must warn you that the code could really benefit from better implementation comments.

Author: facebook
Source Code: https://github.com/facebook/regenerator 
License: MIT license

#javascript #ecmascript #es6 

Source Transformer Enabling ES6 Generator Functions in JS-of-today
Dexter  Goodwin

Dexter Goodwin

1657351680

Flash-store: A High-performance Key-Value Persistent Local Database

flash-store 

FlashStore is Key-Value persistent storage with easy to use ES6 Map-like API(both Async and Sync support), powered by LevelDB and TypeScript.

Requirements

  1. Node.js v10 or above

Examples

Try the following command

npm install
npm run demo

The basic function as follows:

import { FlashStore } from 'flash-store'

const flashStore = new FlashStore('flashstore.workdir')

await flashStore.set(1, 'a')
console.log(`key: 1, value: ${await flashStore.get(1)}`)
// Output: 'a'

await flashStore.del(1)
console.log(`key: 1, value: ${await flashStore.get(1)}`)
// Output: undefined

Supported Backend

BackendFlash StoreInstall NPM Command
SQLitev0.20npm install flash-store@0.20
Medeav0.18npm install flash-store@0.18 (deprecated)
SnapDBv0.16npm install flash-store@0.16 (deprecated)
RocksDBv0.14npm install flash-store@0.14
LevelDBv0.12npm install flash-store@0.12

SnapDB & Medea were all deprecated because of lots of unknown bugs.

API Reference

FlashStore

FlashStore implements the Standard ES6 Map API with Async modification, powered by async-map-like

/**
 * ES6 Map API with Async
 */
export interface AsyncMap<K = any, V = any> {
  [Symbol.asyncIterator]() : AsyncIterableIterator<[K, V]>
  size                     : Promise<number>

  clear   ()                 : Promise<void>
  delete  (key: K)           : Promise<void>
  entries()                  : AsyncIterableIterator<[K, V]>
  get     (key: K)           : Promise<V | undefined>
  has     (key: K)           : Promise<boolean>
  keys    ()                 : AsyncIterableIterator<K>
  set     (key: K, value: V) : Promise<void>
  values  ()                 : AsyncIterableIterator<V>
}

class FlashStore<K, V> implements AsyncMap<K, V> {}

FlashStoreSync

FlashStoreSync implements the Standard ES6 Map API:

class FlashStoreSync<K, V> implements Map<K, V> {}
  1. You get a sync API at the cost of all your data have to be kept in memory.
  2. The data will be async writing back to disk for persistent storage in background.
  3. The performance of FlashStoreSync can be expected high because it's all in memory.

Document

See auto generated docs

See Also

  1. Node.js databases: an embedded database using LevelDB
  2. How to Cook a Graph Database in a Night - LevelGraph
  3. Graph database JS style for Node.js and the Browser. Built upon LevelUp and LevelDB.
  4. 浅析 BigTable 和 LevelDB 的实现

Known Issues

  1. The gte and lte in options do not work property. (#4)

Version History

master

v0.20 Apr 2020 SQLite as Backend

  1. We hardcoded the key type to be string only in this version.
  2. We decide to try better-sqlite3 as it claim is very fast.
  3. The other alternates (would love to try in the future if necessary):
    1. TypeScript: sqlite
    2. WebAssembly: sql.js

v0.18 Feb 2019 - Medea as Backend

Powered by Medea

DEPRECATED: Due to #79 #74 and also it is very unstable in production as my experiences. (e.g. memory leak & block event loop)

  1. Switch from SnapDB to MedeaDown

Medea is a persistent key-value storage library that runs everywhere Node runs.

"It is a pure JS implementation of leveldown and it's almost as fast." — @Raynos link

"The LevelDOWN-compatible wrapper for Medea really opens up the possibility to reuse the modules that have already been created around the LevelUP ecosystem." — @kevinswiber link

Known Issues: FlashStore 会写满磁盘的问题 #155

async function compact (store: FlashStore): Promise<void> {
  await store.size
  const db = (store as any).levelDb.db.db.db
  await new Promise((resolve, reject) => {
    db.compact((err: any) => {
      if (err) {
        return reject(err)
      }
      resolve()
    })
  })
}

v0.16 May 2019 - SnapDB as Backend

Powered by SnapDB

  1. Switch from RocksDB to SnapDB #45
  2. #50 has been fixed. WARN: Do not use this version because it has known issues

v0.14 May 2019 - RocksDB as Backend

  1. Switch from LevelDB to RocksDB #34

v0.12 Jan 2019 - LevelDB as Backend

  1. Use LevelDB as backend to skip the compiling when install.
  2. Using leveldb official typings from @types/

v0.7 Aug 2018 - Nosql-LevelDB as Backend

  1. Use nosql-leveldb as backend to prevent segfault.

v0.6 Jul 2018

  1. Upgrade to TypeScript 3.0

v0.4 Jun 2018

1. Refactor API to implement ES6 Map interface

  1. Update the API to ES6 Map-like, the difference is that FlashStore is all async.

2. Add FlashStoreSync as a in-memory Write-back Cache for Flash-Store

Add a new class FlashStoreSync which is a in-memory full loaded Write-back Cache for Flash-Store:

  1. Writes directly to cache, lazy writes to store.
  2. Reads from cache, never read-miss because cache have the full data of the store which will never expire.
  3. API of FlashStoreSync is the same as the ES6 Map

v0.2 Sep 2017

Init version, API is LevelDB-like.

FAQ

Q: What's the difference between the flash-store and memory-card

Short answer:

  1. flash-store is for save data to local filesystem.
  2. memory-card is for save data to a distributed network storage, it can be serialized/deserialized safely by design.

Long answer:

flash-store and memory-card are all built by @huan, and they are all follow the ES6 Map API.

flash-store is using a no-sql local file database to maximum the performance, it can be used as a local database, or a local cache for whatever you want to cache from other API.

memory-card is using a local file to store data in JSON format by default, however, it supports more distributed methods. Learn more from it's repository at here

Author: huan
Source Code: https://github.com/huan/flash-store 
License: Apache-2.0 license

#javascript #typescript #database #es6 

Flash-store: A High-performance Key-Value Persistent Local Database
Samuel Tucker

Samuel Tucker

1654570734

Terser: JavaScript 'Mangler' and Compressor Toolkit for ES6+

A JavaScript mangler/compressor toolkit for ES6+.

Terser recommends you use RollupJS to bundle your modules, as that produces smaller code overall.

Beautification has been undocumented and is being removed from terser, we recommend you use prettier.

Why choose terser?

uglify-es is no longer maintained and uglify-js does not support ES6+.

terser is a fork of uglify-es that mostly retains API and CLI compatibility with uglify-es and uglify-js@3.

Install

First make sure you have installed the latest version of node.js (You may need to restart your computer after this step).

From NPM for use as a command line app:

npm install terser -g

From NPM for programmatic use:

npm install terser

Command line usage

terser [input files] [options]

Terser can take multiple input files. It's recommended that you pass the input files first, then pass the options. Terser will parse input files in sequence and apply any compression options. The files are parsed in the same global scope, that is, a reference from a file to some variable/function declared in another file will be matched properly.

Command line arguments that take options (like --parse, --compress, --mangle and --format) can take in a comma-separated list of default option overrides. For instance:

terser input.js --compress ecma=2015,computed_props=false

If no input file is specified, Terser will read from STDIN.

If you wish to pass your options before the input files, separate the two with a double dash to prevent input files being used as option arguments:

terser --compress --mangle -- input.js

Command line options

    -h, --help                  Print usage information.
                                `--help options` for details on available options.
    -V, --version               Print version number.
    -p, --parse <options>       Specify parser options:
                                `acorn`  Use Acorn for parsing.
                                `bare_returns`  Allow return outside of functions.
                                                Useful when minifying CommonJS
                                                modules and Userscripts that may
                                                be anonymous function wrapped (IIFE)
                                                by the .user.js engine `caller`.
                                `expression`  Parse a single expression, rather than
                                              a program (for parsing JSON).
                                `spidermonkey`  Assume input files are SpiderMonkey
                                                AST format (as JSON).
    -c, --compress [options]    Enable compressor/specify compressor options:
                                `pure_funcs`  List of functions that can be safely
                                              removed when their return values are
                                              not used.
    -m, --mangle [options]      Mangle names/specify mangler options:
                                `reserved`  List of names that should not be mangled.
    --mangle-props [options]    Mangle properties/specify mangler options:
                                `builtins`  Mangle property names that overlaps
                                            with standard JavaScript globals and DOM
                                            API props.
                                `debug`  Add debug prefix and suffix.
                                `keep_quoted`  Only mangle unquoted properties, quoted
                                               properties are automatically reserved.
                                               `strict` disables quoted properties
                                               being automatically reserved.
                                `regex`  Only mangle matched property names.
                                `reserved`  List of names that should not be mangled.
    -f, --format [options]      Specify format options.
                                `preamble`  Preamble to prepend to the output. You
                                            can use this to insert a comment, for
                                            example for licensing information.
                                            This will not be parsed, but the source
                                            map will adjust for its presence.
                                `quote_style`  Quote style:
                                               0 - auto
                                               1 - single
                                               2 - double
                                               3 - original
                                `wrap_iife`  Wrap IIFEs in parenthesis. Note: you may
                                             want to disable `negate_iife` under
                                             compressor options.
                                `wrap_func_args`  Wrap function arguments in parenthesis.
    -o, --output <file>         Output file path (default STDOUT). Specify `ast` or
                                `spidermonkey` to write Terser or SpiderMonkey AST
                                as JSON to STDOUT respectively.
    --comments [filter]         Preserve copyright comments in the output. By
                                default this works like Google Closure, keeping
                                JSDoc-style comments that contain e.g. "@license",
                                or start with "!". You can optionally pass one of the
                                following arguments to this flag:
                                - "all" to keep all comments
                                - `false` to omit comments in the output
                                - a valid JS RegExp like `/foo/` or `/^!/` to
                                keep only matching comments.
                                Note that currently not *all* comments can be
                                kept when compression is on, because of dead
                                code removal or cascading statements into
                                sequences.
    --config-file <file>        Read `minify()` options from JSON file.
    -d, --define <expr>[=value] Global definitions.
    --ecma <version>            Specify ECMAScript release: 5, 2015, 2016, etc.
    -e, --enclose [arg[:value]] Embed output in a big function with configurable
                                arguments and values.
    --ie8                       Support non-standard Internet Explorer 8.
                                Equivalent to setting `ie8: true` in `minify()`
                                for `compress`, `mangle` and `format` options.
                                By default Terser will not try to be IE-proof.
    --keep-classnames           Do not mangle/drop class names.
    --keep-fnames               Do not mangle/drop function names.  Useful for
                                code relying on Function.prototype.name.
    --module                    Input is an ES6 module. If `compress` or `mangle` is
                                enabled then the `toplevel` option will be enabled.
    --name-cache <file>         File to hold mangled name mappings.
    --safari10                  Support non-standard Safari 10/11.
                                Equivalent to setting `safari10: true` in `minify()`
                                for `mangle` and `format` options.
                                By default `terser` will not work around
                                Safari 10/11 bugs.
    --source-map [options]      Enable source map/specify source map options:
                                `base`  Path to compute relative paths from input files.
                                `content`  Input source map, useful if you're compressing
                                           JS that was generated from some other original
                                           code. Specify "inline" if the source map is
                                           included within the sources.
                                `filename`  Name and/or location of the output source.
                                `includeSources`  Pass this flag if you want to include
                                                  the content of source files in the
                                                  source map as sourcesContent property.
                                `root`  Path to the original source to be included in
                                        the source map.
                                `url`  If specified, path to the source map to append in
                                       `//# sourceMappingURL`.
    --timings                   Display operations run time on STDERR.
    --toplevel                  Compress and/or mangle variables in top level scope.
    --wrap <name>               Embed everything in a big function, making the
                                “exports” and “global” variables available. You
                                need to pass an argument to this option to
                                specify the name that your module will take
                                when included in, say, a browser.

Specify --output (-o) to declare the output file. Otherwise the output goes to STDOUT.

CLI source map options

Terser can generate a source map file, which is highly useful for debugging your compressed JavaScript. To get a source map, pass --source-map --output output.js (source map will be written out to output.js.map).

Additional options:

--source-map "filename='<NAME>'" to specify the name of the source map.

--source-map "root='<URL>'" to pass the URL where the original files can be found.

--source-map "url='<URL>'" to specify the URL where the source map can be found. Otherwise Terser assumes HTTP X-SourceMap is being used and will omit the //# sourceMappingURL= directive.

For example:

terser js/file1.js js/file2.js \
         -o foo.min.js -c -m \
         --source-map "root='http://foo.com/src',url='foo.min.js.map'"

The above will compress and mangle file1.js and file2.js, will drop the output in foo.min.js and the source map in foo.min.js.map. The source mapping will refer to http://foo.com/src/js/file1.js and http://foo.com/src/js/file2.js (in fact it will list http://foo.com/src as the source map root, and the original files as js/file1.js and js/file2.js).

Composed source map

When you're compressing JS code that was output by a compiler such as CoffeeScript, mapping to the JS code won't be too helpful. Instead, you'd like to map back to the original code (i.e. CoffeeScript). Terser has an option to take an input source map. Assuming you have a mapping from CoffeeScript → compiled JS, Terser can generate a map from CoffeeScript → compressed JS by mapping every token in the compiled JS to its original location.

To use this feature pass --source-map "content='/path/to/input/source.map'" or --source-map "content=inline" if the source map is included inline with the sources.

CLI compress options

You need to pass --compress (-c) to enable the compressor. Optionally you can pass a comma-separated list of compress options.

Options are in the form foo=bar, or just foo (the latter implies a boolean option that you want to set true; it's effectively a shortcut for foo=true).

Example:

terser file.js -c toplevel,sequences=false

CLI mangle options

To enable the mangler you need to pass --mangle (-m). The following (comma-separated) options are supported:

toplevel (default false) -- mangle names declared in the top level scope.

eval (default false) -- mangle names visible in scopes where eval or with are used.

When mangling is enabled but you want to prevent certain names from being mangled, you can declare those names with --mangle reserved — pass a comma-separated list of names. For example:

terser ... -m reserved=['$','require','exports']

to prevent the require, exports and $ names from being changed.

CLI mangling property names (--mangle-props)

Note: THIS WILL BREAK YOUR CODE. A good rule of thumb is not to use this unless you know exactly what you're doing and how this works and read this section until the end.

Mangling property names is a separate step, different from variable name mangling. Pass --mangle-props to enable it. The least dangerous way to use this is to use the regex option like so:

terser example.js -c -m --mangle-props regex=/_$/

This will mangle all properties that end with an underscore. So you can use it to mangle internal methods.

By default, it will mangle all properties in the input code with the exception of built in DOM properties and properties in core JavaScript classes, which is what will break your code if you don't:

  1. Control all the code you're mangling
  2. Avoid using a module bundler, as they usually will call Terser on each file individually, making it impossible to pass mangled objects between modules.
  3. Avoid calling functions like defineProperty or hasOwnProperty, because they refer to object properties using strings and will break your code if you don't know what you are doing.

An example:

// example.js
var x = {
    baz_: 0,
    foo_: 1,
    calc: function() {
        return this.foo_ + this.baz_;
    }
};
x.bar_ = 2;
x["baz_"] = 3;
console.log(x.calc());

Mangle all properties (except for JavaScript builtins) (very unsafe):

$ terser example.js -c passes=2 -m --mangle-props
var x={o:3,t:1,i:function(){return this.t+this.o},s:2};console.log(x.i());

Mangle all properties except for reserved properties (still very unsafe):

$ terser example.js -c passes=2 -m --mangle-props reserved=[foo_,bar_]
var x={o:3,foo_:1,t:function(){return this.foo_+this.o},bar_:2};console.log(x.t());

Mangle all properties matching a regex (not as unsafe but still unsafe):

$ terser example.js -c passes=2 -m --mangle-props regex=/_$/
var x={o:3,t:1,calc:function(){return this.t+this.o},i:2};console.log(x.calc());

Combining mangle properties options:

$ terser example.js -c passes=2 -m --mangle-props regex=/_$/,reserved=[bar_]
var x={o:3,t:1,calc:function(){return this.t+this.o},bar_:2};console.log(x.calc());

In order for this to be of any use, we avoid mangling standard JS names and DOM API properties by default (--mangle-props builtins to override).

A regular expression can be used to define which property names should be mangled. For example, --mangle-props regex=/^_/ will only mangle property names that start with an underscore.

When you compress multiple files using this option, in order for them to work together in the end we need to ensure somehow that one property gets mangled to the same name in all of them. For this, pass --name-cache filename.json and Terser will maintain these mappings in a file which can then be reused. It should be initially empty. Example:

$ rm -f /tmp/cache.json  # start fresh
$ terser file1.js file2.js --mangle-props --name-cache /tmp/cache.json -o part1.js
$ terser file3.js file4.js --mangle-props --name-cache /tmp/cache.json -o part2.js

Now, part1.js and part2.js will be consistent with each other in terms of mangled property names.

Using the name cache is not necessary if you compress all your files in a single call to Terser.

Mangling unquoted names (--mangle-props keep_quoted)

Using quoted property name (o["foo"]) reserves the property name (foo) so that it is not mangled throughout the entire script even when used in an unquoted style (o.foo). Example:

// stuff.js
var o = {
    "foo": 1,
    bar: 3
};
o.foo += o.bar;
console.log(o.foo);
$ terser stuff.js --mangle-props keep_quoted -c -m
var o={foo:1,o:3};o.foo+=o.o,console.log(o.foo);

Debugging property name mangling

You can also pass --mangle-props debug in order to mangle property names without completely obscuring them. For example the property o.foo would mangle to o._$foo$_ with this option. This allows property mangling of a large codebase while still being able to debug the code and identify where mangling is breaking things.

$ terser stuff.js --mangle-props debug -c -m
var o={_$foo$_:1,_$bar$_:3};o._$foo$_+=o._$bar$_,console.log(o._$foo$_);

You can also pass a custom suffix using --mangle-props debug=XYZ. This would then mangle o.foo to o._$foo$XYZ_. You can change this each time you compile a script to identify how a property got mangled. One technique is to pass a random number on every compile to simulate mangling changing with different inputs (e.g. as you update the input script with new properties), and to help identify mistakes like writing mangled keys to storage.

API Reference

Assuming installation via NPM, you can load Terser in your application like this:

const { minify } = require("terser");

Or,

import { minify } from "terser";

Browser loading is also supported:

<script src="https://cdn.jsdelivr.net/npm/source-map@0.7.3/dist/source-map.js"></script>
<script src="https://cdn.jsdelivr.net/npm/terser/dist/bundle.min.js"></script>

There is a single async high level function, async minify(code, options), which will perform all minification phases in a configurable manner. By default minify() will enable compress and mangle. Example:

var code = "function add(first, second) { return first + second; }";
var result = await minify(code, { sourceMap: true });
console.log(result.code);  // minified output: function add(n,d){return n+d}
console.log(result.map);  // source map

You can minify more than one JavaScript file at a time by using an object for the first argument where the keys are file names and the values are source code:

var code = {
    "file1.js": "function add(first, second) { return first + second; }",
    "file2.js": "console.log(add(1 + 2, 3 + 4));"
};
var result = await minify(code);
console.log(result.code);
// function add(d,n){return d+n}console.log(add(3,7));

The toplevel option:

var code = {
    "file1.js": "function add(first, second) { return first + second; }",
    "file2.js": "console.log(add(1 + 2, 3 + 4));"
};
var options = { toplevel: true };
var result = await minify(code, options);
console.log(result.code);
// console.log(3+7);

The nameCache option:

var options = {
    mangle: {
        toplevel: true,
    },
    nameCache: {}
};
var result1 = await minify({
    "file1.js": "function add(first, second) { return first + second; }"
}, options);
var result2 = await minify({
    "file2.js": "console.log(add(1 + 2, 3 + 4));"
}, options);
console.log(result1.code);
// function n(n,r){return n+r}
console.log(result2.code);
// console.log(n(3,7));

You may persist the name cache to the file system in the following way:

var cacheFileName = "/tmp/cache.json";
var options = {
    mangle: {
        properties: true,
    },
    nameCache: JSON.parse(fs.readFileSync(cacheFileName, "utf8"))
};
fs.writeFileSync("part1.js", await minify({
    "file1.js": fs.readFileSync("file1.js", "utf8"),
    "file2.js": fs.readFileSync("file2.js", "utf8")
}, options).code, "utf8");
fs.writeFileSync("part2.js", await minify({
    "file3.js": fs.readFileSync("file3.js", "utf8"),
    "file4.js": fs.readFileSync("file4.js", "utf8")
}, options).code, "utf8");
fs.writeFileSync(cacheFileName, JSON.stringify(options.nameCache), "utf8");

An example of a combination of minify() options:

var code = {
    "file1.js": "function add(first, second) { return first + second; }",
    "file2.js": "console.log(add(1 + 2, 3 + 4));"
};
var options = {
    toplevel: true,
    compress: {
        global_defs: {
            "@console.log": "alert"
        },
        passes: 2
    },
    format: {
        preamble: "/* minified */"
    }
};
var result = await minify(code, options);
console.log(result.code);
// /* minified */
// alert(10);"

An error example:

try {
    const result = await minify({"foo.js" : "if (0) else console.log(1);"});
    // Do something with result
} catch (error) {
    const { message, filename, line, col, pos } = error;
    // Do something with error
}

Minify options

ecma (default undefined) - pass 5, 2015, 2016, etc to override compress and format's ecma options.

enclose (default false) - pass true, or a string in the format of "args[:values]", where args and values are comma-separated argument names and values, respectively, to embed the output in a big function with the configurable arguments and values.

parse (default {}) — pass an object if you wish to specify some additional parse options.

compress (default {}) — pass false to skip compressing entirely. Pass an object to specify custom compress options.

mangle (default true) — pass false to skip mangling names, or pass an object to specify mangle options (see below).

  • mangle.properties (default false) — a subcategory of the mangle option. Pass an object to specify custom mangle property options.

module (default false) — Use when minifying an ES6 module. "use strict" is implied and names can be mangled on the top scope. If compress or mangle is enabled then the toplevel option will be enabled.

format or output (default null) — pass an object if you wish to specify additional format options. The defaults are optimized for best compression.

sourceMap (default false) - pass an object if you wish to specify source map options.

toplevel (default false) - set to true if you wish to enable top level variable and function name mangling and to drop unused variables and functions.

nameCache (default null) - pass an empty object {} or a previously used nameCache object if you wish to cache mangled variable and property names across multiple invocations of minify(). Note: this is a read/write property. minify() will read the name cache state of this object and update it during minification so that it may be reused or externally persisted by the user.

ie8 (default false) - set to true to support IE8.

keep_classnames (default: undefined) - pass true to prevent discarding or mangling of class names. Pass a regular expression to only keep class names matching that regex.

keep_fnames (default: false) - pass true to prevent discarding or mangling of function names. Pass a regular expression to only keep function names matching that regex. Useful for code relying on Function.prototype.name. If the top level minify option keep_classnames is undefined it will be overridden with the value of the top level minify option keep_fnames.

safari10 (default: false) - pass true to work around Safari 10/11 bugs in loop scoping and await. See safari10 options in mangle and format for details.

Minify options structure

{
    parse: {
        // parse options
    },
    compress: {
        // compress options
    },
    mangle: {
        // mangle options

        properties: {
            // mangle property options
        }
    },
    format: {
        // format options (can also use `output` for backwards compatibility)
    },
    sourceMap: {
        // source map options
    },
    ecma: 5, // specify one of: 5, 2015, 2016, etc.
    enclose: false, // or specify true, or "args:values"
    keep_classnames: false,
    keep_fnames: false,
    ie8: false,
    module: false,
    nameCache: null, // or specify a name cache object
    safari10: false,
    toplevel: false
}

Source map options

To generate a source map:

var result = await minify({"file1.js": "var a = function() {};"}, {
    sourceMap: {
        filename: "out.js",
        url: "out.js.map"
    }
});
console.log(result.code); // minified output
console.log(result.map);  // source map

Note that the source map is not saved in a file, it's just returned in result.map. The value passed for sourceMap.url is only used to set //# sourceMappingURL=out.js.map in result.code. The value of filename is only used to set file attribute (see the spec) in source map file.

You can set option sourceMap.url to be "inline" and source map will be appended to code.

You can also specify sourceRoot property to be included in source map:

var result = await minify({"file1.js": "var a = function() {};"}, {
    sourceMap: {
        root: "http://example.com/src",
        url: "out.js.map"
    }
});

If you're compressing compiled JavaScript and have a source map for it, you can use sourceMap.content:

var result = await minify({"compiled.js": "compiled code"}, {
    sourceMap: {
        content: "content from compiled.js.map",
        url: "minified.js.map"
    }
});
// same as before, it returns `code` and `map`

If you're using the X-SourceMap header instead, you can just omit sourceMap.url.

If you happen to need the source map as a raw object, set sourceMap.asObject to true.

Parse options

bare_returns (default false) -- support top level return statements

html5_comments (default true)

shebang (default true) -- support #!command as the first line

spidermonkey (default false) -- accept a Spidermonkey (Mozilla) AST

Compress options

defaults (default: true) -- Pass false to disable most default enabled compress transforms. Useful when you only want to enable a few compress options while disabling the rest.

arrows (default: true) -- Class and object literal methods are converted will also be converted to arrow expressions if the resultant code is shorter: m(){return x} becomes m:()=>x. To do this to regular ES5 functions which don't use this or arguments, see unsafe_arrows.

arguments (default: false) -- replace arguments[index] with function parameter name whenever possible.

booleans (default: true) -- various optimizations for boolean context, for example !!a ? b : c → a ? b : c

booleans_as_integers (default: false) -- Turn booleans into 0 and 1, also makes comparisons with booleans use == and != instead of === and !==.

collapse_vars (default: true) -- Collapse single-use non-constant variables, side effects permitting.

comparisons (default: true) -- apply certain optimizations to binary nodes, e.g. !(a <= b) → a > b (only when unsafe_comps), attempts to negate binary nodes, e.g. a = !b && !c && !d && !e → a=!(b||c||d||e) etc.

computed_props (default: true) -- Transforms constant computed properties into regular ones: {["computed"]: 1} is converted to {computed: 1}.

conditionals (default: true) -- apply optimizations for if-s and conditional expressions

dead_code (default: true) -- remove unreachable code

directives (default: true) -- remove redundant or non-standard directives

drop_console (default: false) -- Pass true to discard calls to console.* functions. If you wish to drop a specific function call such as console.info and/or retain side effects from function arguments after dropping the function call then use pure_funcs instead.

drop_debugger (default: true) -- remove debugger; statements

ecma (default: 5) -- Pass 2015 or greater to enable compress options that will transform ES5 code into smaller ES6+ equivalent forms.

evaluate (default: true) -- attempt to evaluate constant expressions

expression (default: false) -- Pass true to preserve completion values from terminal statements without return, e.g. in bookmarklets.

global_defs (default: {}) -- see conditional compilation

hoist_funs (default: false) -- hoist function declarations

hoist_props (default: true) -- hoist properties from constant object and array literals into regular variables subject to a set of constraints. For example: var o={p:1, q:2}; f(o.p, o.q); is converted to f(1, 2);. Note: hoist_props works best with mangle enabled, the compress option passes set to 2 or higher, and the compress option toplevel enabled.

hoist_vars (default: false) -- hoist var declarations (this is false by default because it seems to increase the size of the output in general)

if_return (default: true) -- optimizations for if/return and if/continue

inline (default: true) -- inline calls to function with simple/return statement:

  • false -- same as 0
  • 0 -- disabled inlining
  • 1 -- inline simple functions
  • 2 -- inline functions with arguments
  • 3 -- inline functions with arguments and variables
  • true -- same as 3

join_vars (default: true) -- join consecutive var statements

keep_classnames (default: false) -- Pass true to prevent the compressor from discarding class names. Pass a regular expression to only keep class names matching that regex. See also: the keep_classnames mangle option.

keep_fargs (default: true) -- Prevents the compressor from discarding unused function arguments. You need this for code which relies on Function.length.

keep_fnames (default: false) -- Pass true to prevent the compressor from discarding function names. Pass a regular expression to only keep function names matching that regex. Useful for code relying on Function.prototype.name. See also: the keep_fnames mangle option.

keep_infinity (default: false) -- Pass true to prevent Infinity from being compressed into 1/0, which may cause performance issues on Chrome.

loops (default: true) -- optimizations for do, while and for loops when we can statically determine the condition.

module (default false) -- Pass true when compressing an ES6 module. Strict mode is implied and the toplevel option as well.

negate_iife (default: true) -- negate "Immediately-Called Function Expressions" where the return value is discarded, to avoid the parens that the code generator would insert.

passes (default: 1) -- The maximum number of times to run compress. In some cases more than one pass leads to further compressed code. Keep in mind more passes will take more time.

properties (default: true) -- rewrite property access using the dot notation, for example foo["bar"] → foo.bar

pure_funcs (default: null) -- You can pass an array of names and Terser will assume that those functions do not produce side effects. DANGER: will not check if the name is redefined in scope. An example case here, for instance var q = Math.floor(a/b). If variable q is not used elsewhere, Terser will drop it, but will still keep the Math.floor(a/b), not knowing what it does. You can pass pure_funcs: [ 'Math.floor' ] to let it know that this function won't produce any side effect, in which case the whole statement would get discarded. The current implementation adds some overhead (compression will be slower).

pure_getters (default: "strict") -- If you pass true for this, Terser will assume that object property access (e.g. foo.bar or foo["bar"]) doesn't have any side effects. Specify "strict" to treat foo.bar as side-effect-free only when foo is certain to not throw, i.e. not null or undefined.

reduce_vars (default: true) -- Improve optimization on variables assigned with and used as constant values.

reduce_funcs (default: true) -- Inline single-use functions when possible. Depends on reduce_vars being enabled. Disabling this option sometimes improves performance of the output code.

sequences (default: true) -- join consecutive simple statements using the comma operator. May be set to a positive integer to specify the maximum number of consecutive comma sequences that will be generated. If this option is set to true then the default sequences limit is 200. Set option to false or 0 to disable. The smallest sequences length is 2. A sequences value of 1 is grandfathered to be equivalent to true and as such means 200. On rare occasions the default sequences limit leads to very slow compress times in which case a value of 20 or less is recommended.

side_effects (default: true) -- Remove expressions which have no side effects and whose results aren't used.

switches (default: true) -- de-duplicate and remove unreachable switch branches

toplevel (default: false) -- drop unreferenced functions ("funcs") and/or variables ("vars") in the top level scope (false by default, true to drop both unreferenced functions and variables)

top_retain (default: null) -- prevent specific toplevel functions and variables from unused removal (can be array, comma-separated, RegExp or function. Implies toplevel)

typeofs (default: true) -- Transforms typeof foo == "undefined" into foo === void 0. Note: recommend to set this value to false for IE10 and earlier versions due to known issues.

unsafe (default: false) -- apply "unsafe" transformations (details).

unsafe_arrows (default: false) -- Convert ES5 style anonymous function expressions to arrow functions if the function body does not reference this. Note: it is not always safe to perform this conversion if code relies on the the function having a prototype, which arrow functions lack. This transform requires that the ecma compress option is set to 2015 or greater.

unsafe_comps (default: false) -- Reverse < and <= to > and >= to allow improved compression. This might be unsafe when an at least one of two operands is an object with computed values due the use of methods like get, or valueOf. This could cause change in execution order after operands in the comparison are switching. Compression only works if both comparisons and unsafe_comps are both set to true.

unsafe_Function (default: false) -- compress and mangle Function(args, code) when both args and code are string literals.

unsafe_math (default: false) -- optimize numerical expressions like 2 * x * 3 into 6 * x, which may give imprecise floating point results.

unsafe_symbols (default: false) -- removes keys from native Symbol declarations, e.g Symbol("kDog") becomes Symbol().

unsafe_methods (default: false) -- Converts { m: function(){} } to { m(){} }. ecma must be set to 6 or greater to enable this transform. If unsafe_methods is a RegExp then key/value pairs with keys matching the RegExp will be converted to concise methods. Note: if enabled there is a risk of getting a "<method name> is not a constructor" TypeError should any code try to new the former function.

unsafe_proto (default: false) -- optimize expressions like Array.prototype.slice.call(a) into [].slice.call(a)

unsafe_regexp (default: false) -- enable substitutions of variables with RegExp values the same way as if they are constants.

unsafe_undefined (default: false) -- substitute void 0 if there is a variable named undefined in scope (variable name will be mangled, typically reduced to a single character)

unused (default: true) -- drop unreferenced functions and variables (simple direct variable assignments do not count as references unless set to "keep_assign")

Mangle options

eval (default false) -- Pass true to mangle names visible in scopes where eval or with are used.

keep_classnames (default false) -- Pass true to not mangle class names. Pass a regular expression to only keep class names matching that regex. See also: the keep_classnames compress option.

keep_fnames (default false) -- Pass true to not mangle function names. Pass a regular expression to only keep function names matching that regex. Useful for code relying on Function.prototype.name. See also: the keep_fnames compress option.

module (default false) -- Pass true an ES6 modules, where the toplevel scope is not the global scope. Implies toplevel.

nth_identifier (default: an internal mangler that weights based on character frequency analysis) -- Pass an object with a get(n) function that converts an ordinal into the nth most favored (usually shortest) identifier. Optionally also provide reset(), sort(), and consider(chars, delta) to use character frequency analysis of the source code.

reserved (default []) -- Pass an array of identifiers that should be excluded from mangling. Example: ["foo", "bar"].

toplevel (default false) -- Pass true to mangle names declared in the top level scope.

safari10 (default false) -- Pass true to work around the Safari 10 loop iterator bug "Cannot declare a let variable twice". See also: the safari10 format option.

Examples:

// test.js
var globalVar;
function funcName(firstLongName, anotherLongName) {
    var myVariable = firstLongName +  anotherLongName;
}
var code = fs.readFileSync("test.js", "utf8");

await minify(code).code;
// 'function funcName(a,n){}var globalVar;'

await minify(code, { mangle: { reserved: ['firstLongName'] } }).code;
// 'function funcName(firstLongName,a){}var globalVar;'

await minify(code, { mangle: { toplevel: true } }).code;
// 'function n(n,a){}var a;'

Mangle properties options

builtins (default: false) — Use true to allow the mangling of builtin DOM properties. Not recommended to override this setting.

debug (default: false) — Mangle names with the original name still present. Pass an empty string "" to enable, or a non-empty string to set the debug suffix.

keep_quoted (default: false) — How quoting properties ({"prop": ...} and obj["prop"]) controls what gets mangled.

  • "strict" (recommended) -- obj.prop is mangled.
  • false -- obj["prop"] is mangled.
  • true -- obj.prop is mangled unless there is obj["prop"] elsewhere in the code.

nth_identifer (default: an internal mangler that weights based on character frequency analysis) -- Pass an object with a get(n) function that converts an ordinal into the nth most favored (usually shortest) identifier. Optionally also provide reset(), sort(), and consider(chars, delta) to use character frequency analysis of the source code.

regex (default: null) — Pass a RegExp literal or pattern string to only mangle property matching the regular expression.

reserved (default: []) — Do not mangle property names listed in the reserved array.

undeclared (default: false) - Mangle those names when they are accessed as properties of known top level variables but their declarations are never found in input code. May be useful when only minifying parts of a project. See #397 for more details.

Format options

These options control the format of Terser's output code. Previously known as "output options".

ascii_only (default false) -- escape Unicode characters in strings and regexps (affects directives with non-ascii characters becoming invalid)

beautify (default false) -- (DEPRECATED) whether to beautify the output. When using the legacy -b CLI flag, this is set to true by default.

braces (default false) -- always insert braces in if, for, do, while or with statements, even if their body is a single statement.

comments (default "some") -- by default it keeps JSDoc-style comments that contain "@license", "@copyright", "@preserve" or start with !, pass true or "all" to preserve all comments, false to omit comments in the output, a regular expression string (e.g. /^!/) or a function.

ecma (default 5) -- set desired EcmaScript standard version for output. Set ecma to 2015 or greater to emit shorthand object properties - i.e.: {a} instead of {a: a}. The ecma option will only change the output in direct control of the beautifier. Non-compatible features in your input will still be output as is. For example: an ecma setting of 5 will not convert modern code to ES5.

indent_level (default 4)

indent_start (default 0) -- prefix all lines by that many spaces

inline_script (default true) -- escape HTML comments and the slash in occurrences of </script> in strings

keep_numbers (default false) -- keep number literals as it was in original code (disables optimizations like converting 1000000 into 1e6)

keep_quoted_props (default false) -- when turned on, prevents stripping quotes from property names in object literals.

max_line_len (default false) -- maximum line length (for minified code)

preamble (default null) -- when passed it must be a string and it will be prepended to the output literally. The source map will adjust for this text. Can be used to insert a comment containing licensing information, for example.

quote_keys (default false) -- pass true to quote all keys in literal objects

quote_style (default 0) -- preferred quote style for strings (affects quoted property names and directives as well):

  • 0 -- prefers double quotes, switches to single quotes when there are more double quotes in the string itself. 0 is best for gzip size.
  • 1 -- always use single quotes
  • 2 -- always use double quotes
  • 3 -- always use the original quotes

preserve_annotations -- (default false) -- Preserve Terser annotations in the output.

safari10 (default false) -- set this option to true to work around the Safari 10/11 await bug. See also: the safari10 mangle option.

semicolons (default true) -- separate statements with semicolons. If you pass false then whenever possible we will use a newline instead of a semicolon, leading to more readable output of minified code (size before gzip could be smaller; size after gzip insignificantly larger).

shebang (default true) -- preserve shebang #! in preamble (bash scripts)

spidermonkey (default false) -- produce a Spidermonkey (Mozilla) AST

webkit (default false) -- enable workarounds for WebKit bugs. PhantomJS users should set this option to true.

wrap_iife (default false) -- pass true to wrap immediately invoked function expressions. See #640 for more details.

wrap_func_args (default true) -- pass false if you do not want to wrap function expressions that are passed as arguments, in parenthesis. See OptimizeJS for more details.

Miscellaneous

Keeping copyright notices or other comments

You can pass --comments to retain certain comments in the output. By default it will keep comments starting with "!" and JSDoc-style comments that contain "@preserve", "@copyright", "@license" or "@cc_on" (conditional compilation for IE). You can pass --comments all to keep all the comments, or a valid JavaScript regexp to keep only comments that match this regexp. For example --comments /^!/ will keep comments like /*! Copyright Notice */.

Note, however, that there might be situations where comments are lost. For example:

function f() {
    /** @preserve Foo Bar */
    function g() {
        // this function is never called
    }
    return something();
}

Even though it has "@preserve", the comment will be lost because the inner function g (which is the AST node to which the comment is attached to) is discarded by the compressor as not referenced.

The safest comments where to place copyright information (or other info that needs to be kept in the output) are comments attached to toplevel nodes.

The unsafe compress option

It enables some transformations that might break code logic in certain contrived cases, but should be fine for most code. It assumes that standard built-in ECMAScript functions and classes have not been altered or replaced. You might want to try it on your own code; it should reduce the minified size. Some examples of the optimizations made when this option is enabled:

  • new Array(1, 2, 3) or Array(1, 2, 3)[ 1, 2, 3 ]
  • Array.from([1, 2, 3])[1, 2, 3]
  • new Object(){}
  • String(exp) or exp.toString()"" + exp
  • new Object/RegExp/Function/Error/Array (...) → we discard the new
  • "foo bar".substr(4)"bar"

Conditional compilation

You can use the --define (-d) switch in order to declare global variables that Terser will assume to be constants (unless defined in scope). For example if you pass --define DEBUG=false then, coupled with dead code removal Terser will discard the following from the output:

if (DEBUG) {
    console.log("debug stuff");
}

You can specify nested constants in the form of --define env.DEBUG=false.

Another way of doing that is to declare your globals as constants in a separate file and include it into the build. For example you can have a build/defines.js file with the following:

var DEBUG = false;
var PRODUCTION = true;
// etc.

and build your code like this:

terser build/defines.js js/foo.js js/bar.js... -c

Terser will notice the constants and, since they cannot be altered, it will evaluate references to them to the value itself and drop unreachable code as usual. The build will contain the const declarations if you use them. If you are targeting < ES6 environments which does not support const, using var with reduce_vars (enabled by default) should suffice.

Conditional compilation API

You can also use conditional compilation via the programmatic API. With the difference that the property name is global_defs and is a compressor property:

var result = await minify(fs.readFileSync("input.js", "utf8"), {
    compress: {
        dead_code: true,
        global_defs: {
            DEBUG: false
        }
    }
});

To replace an identifier with an arbitrary non-constant expression it is necessary to prefix the global_defs key with "@" to instruct Terser to parse the value as an expression:

await minify("alert('hello');", {
    compress: {
        global_defs: {
            "@alert": "console.log"
        }
    }
}).code;
// returns: 'console.log("hello");'

Otherwise it would be replaced as string literal:

await minify("alert('hello');", {
    compress: {
        global_defs: {
            "alert": "console.log"
        }
    }
}).code;
// returns: '"console.log"("hello");'

Annotations

Annotations in Terser are a way to tell it to treat a certain function call differently. The following annotations are available:

  • /*@__INLINE__*/ - forces a function to be inlined somewhere.
  • /*@__NOINLINE__*/ - Makes sure the called function is not inlined into the call site.
  • /*@__PURE__*/ - Marks a function call as pure. That means, it can safely be dropped.

You can use either a @ sign at the start, or a #.

Here are some examples on how to use them:

/*@__INLINE__*/
function_always_inlined_here()

/*#__NOINLINE__*/
function_cant_be_inlined_into_here()

const x = /*#__PURE__*/i_am_dropped_if_x_is_not_used()

ESTree / SpiderMonkey AST

Terser has its own abstract syntax tree format; for practical reasons we can't easily change to using the SpiderMonkey AST internally. However, Terser now has a converter which can import a SpiderMonkey AST.

For example Acorn is a super-fast parser that produces a SpiderMonkey AST. It has a small CLI utility that parses one file and dumps the AST in JSON on the standard output. To use Terser to mangle and compress that:

acorn file.js | terser -p spidermonkey -m -c

The -p spidermonkey option tells Terser that all input files are not JavaScript, but JS code described in SpiderMonkey AST in JSON. Therefore we don't use our own parser in this case, but just transform that AST into our internal AST.

spidermonkey is also available in minify as parse and format options to accept and/or produce a spidermonkey AST.

Use Acorn for parsing

More for fun, I added the -p acorn option which will use Acorn to do all the parsing. If you pass this option, Terser will require("acorn").

Acorn is really fast (e.g. 250ms instead of 380ms on some 650K code), but converting the SpiderMonkey tree that Acorn produces takes another 150ms so in total it's a bit more than just using Terser's own parser.

Terser Fast Minify Mode

It's not well known, but whitespace removal and symbol mangling accounts for 95% of the size reduction in minified code for most JavaScript - not elaborate code transforms. One can simply disable compress to speed up Terser builds by 3 to 4 times.

d3.jssizegzip sizetime (s)
original451,131108,733-
terser@3.7.5 mangle=false, compress=false316,60085,2450.82
terser@3.7.5 mangle=true, compress=false220,21672,7301.45
terser@3.7.5 mangle=true, compress=true212,04670,9545.87
babili@0.1.4210,71372,14012.64
babel-minify@0.4.3210,32172,24248.67
babel-minify@0.5.0-alpha.01eac1c3210,42172,23814.17

To enable fast minify mode from the CLI use:

terser file.js -m

To enable fast minify mode with the API use:

await minify(code, { compress: false, mangle: true });

Source maps and debugging

Various compress transforms that simplify, rearrange, inline and remove code are known to have an adverse effect on debugging with source maps. This is expected as code is optimized and mappings are often simply not possible as some code no longer exists. For highest fidelity in source map debugging disable the compress option and just use mangle.

Compiler assumptions

To allow for better optimizations, the compiler makes various assumptions:

  • .toString() and .valueOf() don't have side effects, and for built-in objects they have not been overridden.
  • undefined, NaN and Infinity have not been externally redefined.
  • arguments.callee, arguments.caller and Function.prototype.caller are not used.
  • The code doesn't expect the contents of Function.prototype.toString() or Error.prototype.stack to be anything in particular.
  • Getting and setting properties on a plain object does not cause other side effects (using .watch() or Proxy).
  • Object properties can be added, removed and modified (not prevented with Object.defineProperty(), Object.defineProperties(), Object.freeze(), Object.preventExtensions() or Object.seal()).
  • document.all is not == null
  • Assigning properties to a class doesn't have side effects and does not throw.

Build Tools and Adaptors using Terser

https://www.npmjs.com/browse/depended/terser

Replacing uglify-es with terser in a project using yarn

A number of JS bundlers and uglify wrappers are still using buggy versions of uglify-es and have not yet upgraded to terser. If you are using yarn you can add the following alias to your project's package.json file:

  "resolutions": {
    "uglify-es": "npm:terser"
  }

to use terser instead of uglify-es in all deeply nested dependencies without changing any code.

Note: for this change to take effect you must run the following commands to remove the existing yarn lock file and reinstall all packages:

$ rm -rf node_modules yarn.lock
$ yarn

Reporting issues

In the terser CLI we use source-map-support to produce good error stacks. In your own app, you're expected to enable source-map-support (read their docs) to have nice stack traces that will help you write good issues.

Obtaining the source code given to Terser

Because users often don't control the call to await minify() or its arguments, Terser provides a TERSER_DEBUG_DIR environment variable to make terser output some debug logs. If you're using a bundler or a project that includes a bundler and are not sure what went wrong with your code, pass that variable like so:

$ TERSER_DEBUG_DIR=/path/to/logs command-that-uses-terser
$ ls /path/to/logs
terser-debug-123456.log

If you're not sure how to set an environment variable on your shell (the above example works in bash), you can try using cross-env:

> npx cross-env TERSER_DEBUG_DIR=/path/to/logs command-that-uses-terser

Download Details: 
Author: terser
Source Code: https://github.com/terser/terser 
 

#testing #javascript #es6 #programming 
 

Terser: JavaScript 'Mangler' and Compressor Toolkit for ES6+

Yo-Yo: Building Modular UI Components using DOM Diffing & ES6

yo-yo.js

A tiny library for building modular UI components using DOM diffing and ES6 tagged template literals, powered by bel and morphdom and based on the "yo-yo" data binding pattern: data down, actions up.

yo-yo powers the choo framework, you should check it out if you want something higher level! or if you want lower level, see the module that powers yo-yo: bel

logo

Getting started is as easy as

var element = yo`<h1>hello world!</h1>`

Give yo-yo a spin in your browser on RequireBin.

Features

  • React-style modular UI components that can efficiently update themselves
  • Build your own framework: small modules that you can swap out to pick your own tradeoffs
  • Uses features available in browsers today instead of inventing new syntax/APIs
  • Designed for template literals, a templating feature built in to JS
  • Uses a default DOM diffing strategy based on the real DOM, not a virtual DOM
  • Compatible with vanilla DOM elements and vanilla JS data structures
  • Doesn't require hundreds of megabytes of devDependencies to build
  • 4kb minified + gzipped (6 times smaller than React), small enough for UI components to include as a dependency

About

yo-yo is a modular UI framework, meaning there isn't much code in this repository, much of the functionality comes from other modules (see index.js). The goals of yo-yo are to choose a good set of default dependencies, document how to use them all together in one place, and use small enough dependencies that you can include a copy of yo-yo in standalone UI component modules and publish them to npm.

You can start by simply doing require('yo-yo') but as your app grows will most likely want to choose different tradeoffs (add or remove dependencies), and yo-yo is designed to let you do that without rewriting all of your code due to API changes, forcing you to use certain dependencies, or making you adopt new coding conventions.

In this way yo-yo is similar to the modular frameworks mississippi, http-framework and mercury.

Installing

You can get it from npm: npm install yo-yo

To create a standalone copy run browserify --standalone yo index.js > yo-yo.js

API

The yo-yo API is very simple and only has two functions.

var yo = require('yo-yo')

Returns the yo function. There is also a method on yo called yo.update.

yo`template`

yo is a function designed to be used with tagged template literals. If your template produces a string containing an HTML element, the yo function will take it and produce a new DOM element that you can insert into the DOM.

yo.update(targetElement, newElement, [opts])

Efficiently updates the attributes and content of an element by diffing and morphing a new element onto an existing target element. The two elements + their children should have the same 'shape', as the diff between newElement will replace nodes in targetElement. targetElement will get efficiently updated with only the new DOM nodes from newElement, and newElement can be discarded afterwards.

Note that many properties of a DOM element are ignored when elements are updated. morphdom only copies the following properties:

  • node.firstChild
  • node.tagName
  • node.nextSibling
  • node.attributes
  • node.nodeType
  • node.nodeValue

In addition to these yo-yo will copy event attributes (e.g. onclick, onmousedown) that you set using DOM attributes in your template.

opts is optional and has these options:

  • events - set false to disable copying of event attributes. otherwise set to an array of strings, one for each event name you want to whitelist for copying. defaults to our default events

The opts object will also get passed to morphdom.

Examples

Here are some UI modules implemented using yo-yo:

And here are some simpler examples:

Creating a simple list

var yo = require('yo-yo')

var el = list([
  'grizzly',
  'polar',
  'brown'
])

function list (items) {
  return yo`<ul>
    ${items.map(function (item) {
      return yo`<li>${item}</li>`
    })}
  </ul>`
}

document.body.appendChild(el)

Dynamic updates

var yo = require('yo-yo')

var numbers = [] // start empty
var el = list(numbers, update)

function list (items, onclick) {
  return yo`<div>
    Random Numbers
    <ul>
      ${items.map(function (item) {
        return yo`<li>${item}</li>`
      })}
    </ul>
    <button onclick=${onclick}>Add Random Number</button>
  </div>`
}

function update () {
  // add a new random number to our list
  numbers.push(Math.random())
  
  // construct a new list and efficiently diff+morph it into the one in the DOM
  var newList = list(numbers, update)
  yo.update(el, newList)
}

document.body.appendChild(el)

Clicking the button three times results in this HTML:

<div>Random Numbers
  <ul>
    <li>0.027827488956972957</li>
    <li>0.742044786689803</li>
    <li>0.4440679911058396</li>
  </ul>
  <button>Add Random Number</button>
</div>

When the button is clicked, thanks to yo.update, only a single new <li> is inserted into the DOM.

Updating events

Event handlers starting with on that you set via attributes will get updated.

function a () { console.log('a') }
function b () { console.log('b') }

var el = yo`<button onclick=${a}>hi</button>`
el.click() // logs 'a' to console

var newEl = yo`<button onclick=${b}>hi</button>`
yo.update(el, newEl)
el.click() // logs 'b' to console

This works because we explicitly copy common event attributes. When yo.update is called above, el is still the same JavaScript Object instance before and after. The only difference is that yo.update will copy any new attributes from newEl onto el. However, if you add custom properties or events to newEl before calling yo.update, for example newEl.addEventListener('foo', handleFoo), they will not be copied onto el.

Modules that work well with yo-yo

The functionality built in to yo-yo covers the same problems as React and JSX, (DOM diffing and templating), using these dependencies of yo-yo:

  • bel - creates DOM elements from template strings
  • morphdom - efficiently morphs DOM elements (without a virtual DOM)

However you might consider these alternatives to the above built-in choices based on your use case:

There are also UI problems that yo-yo does not currently address, such as events. But it's easy to use other modules alongside yo-yo to create your own framework. We might even add some of these to yo-yo in the future:

Older Browser Compatibility / Production Performance

If you are targeting browsers that may not support template literals and would like to get a performance boost by transforming your yo-yo elements into raw document calls:

CSS

  • dom-css - inline CSS helper
  • csjs - namespaced CSS helper
  • csjs-extractify - csjs browserify transform to compile css bundles
  • csjs-injectify - csjs browserify transform that uses insert-css
  • sheetify - browserify modular css transform
  • plain css files - you don't always have to use a fancy CSS module :)

State management

In yo-yo state management is left completely up to you. The simplest approach is the "yo-yo" pattern: simply call a callback up until it reaches a parent where you want to handle updates, then yo.update() the changes down from there, which keeps the elements isolated. But since you are just working with DOM elements, you can do yo.update(document.querySelector('.some-other-element'), newelement) as well.

There are also some other approaches that introduce their own patterns for managing state:

Overview of default dependencies

bel

bel is a module that takes the output from a tagged template string and creates or updates (using DOM diffing) a DOM element tree.

Tagged template literals

Tagged template literals are a way to use template literals (AKA template strings) with functions that take the output of the template string and format them in a certain way.

Regular template literals lets you take code like this:

var multiline = 'hello\n' +
'this\n' +
'is\n' +
'multiline'

And write the same thing like this instead:

var multiline = `hello
this
is
multiline`

Tagged template literals is where you put a function name in front of the template tags, similar to calling a function with () but using the backticks ```` instead of parens.

function doesNothing () {}

doesNothing`im a string`

The above example causes the doesNothing function to get invoked (AKA called), similar to if you did doesNothing('im a string').

The difference is that tagged template strings return a specific output value.

function logArguments (a, b, c, d) {
  console.log(a, b, c, d)
}

logArguments`im a string`

Running the above produces ["im a string", raw: "im a string"] undefined undefined undefined.

If you were to just run console.log(im a string) it would produce "im a string".

However, tagged template strings return the above tagged template array output format.

The first item in the array is an array of all of the strings in your template string. In our case there is only one:

["im a string", raw: "im a string"]

The raw is a property that also contains an array, but where the values are the 'raw' values as there were entered.

If you had this template for example:

logArguments`\u9999`

It would produce this as the first argument to logArguments: ["香", raw: ["\u9999"]]

In template literals, tagged or not, you can interpolate values by embedding javascript expressions inside of ${}

var name = 'bob'
console.log(`hello ${name}!`)

The above produces "hello bob!". However, when called like this:

function logArguments (a, b, c, d) {  console.log(a, b, c, d) } var name = 'bob' logArguments`hello ${name}!`

It produces the tagged template array ["hello ", "!", raw: ["hello ", "!"]] "bob" undefined undefined

As you can see the first argument is an array of all of the strings, and the rest of the arguments are all of the interpolated values one at a time.

Using this array you can implement your own custom way to render the strings and values. For example to simply print a string you print the strings and values in 'zipped' order):

function printString(strings, valueA, valueB, valueC) {
  console.log(strings[0] + valueA + strings[1] + valueB + strings[2] + valueC)
}

You could also imagine writing the above function in a more general way using loops etc. Or do something entirely different:

hyperx

yo-yo uses a module called bel which in turn uses hyperx to turn tagged template arrays into DOM builder data.

For example:

var hyperx = require('hyperx')

var convertTaggedTemplateOutputToDomBuilder = hyperx(function (tagName, attrs, children) {
  console.log(tagName, attrs, children)
})

convertTaggedTemplateOutputToDomBuilder`<h1>hello world</h1>`

Running this produces h1 {} [ 'hello world' ], which aren't yet DOM elements but have all the data you need to build your own DOM elements however you like. These three arguments, tagName, attrs, children are a sort of pseudo-standard used by various DOM building libraries such as virtual-dom, hyperscript and react, and now hyperx and bel.

You can also use DOM elements not created using hyperx and bel:

var yo = require('yo-yo')
var vanillaElement = document.createElement('h3')
vanillaElement.textContent = 'Hello'

var app = yo`<div class="app">${vanillaElement} World</div>`

Running the above sets app to an element with this HTML:

<div class="app"><h3>Hello</h3> World</div>

morphdom

yo-yo lets you do two basic things: create an element and update it. When you create an element it simply creates a new DOM element tree using hyperx and its own custom code that uses document.createElement.

However, when you update an element using yo.update() it actually uses a module called morphdom to transform the existing DOM tree to match the new DOM tree while minimizing the number of changes to the existing DOM tree. This is a really similar approach to what react and virtual-dom do, except morphdom does not use a virtual DOM, it simply uses the actual DOM.

Benchmarks

You can find benchmarks at https://github.com/shama/yo-yo-perf

Author: Maxogden
Source Code: https://github.com/maxogden/yo-yo 
License: 

#javascript #es6 #dom 

Yo-Yo: Building Modular UI Components using DOM Diffing & ES6

Cash: Cross-platform Linux Commands in ES6

Cross-platform Linux commands in pure ES6

Cash is a cross-platform implementation of Unix shell commands written in straight ES6. No native compiling and no external dependencies.

While young, Cash aims to offer an alternative Linux feel on Windows and to open the door to cross-platform bash scripting in a Javascript environment.

> npm install cash -g
> cash
$

Cash

Woah.

Yeah. But it gets better.

Let's mix some Windows & Unix commands together:

$ ipconfig | grep IPv4 | sort
IPv4 Address. . . . . . . . . . . : 10.10.40.50
IPv4 Address. . . . . . . . . . . : 192.168.100.11
$

Learn more

But I don't want to type "cash"

No problem. Let's make all commands global on your system:

> npm install cash-global -g
> ls -lah

Learn more

Nice, but I only want certain commands

You're covered!

> npm install cash-ls -g
> npm install cash-grep -g

Learn more

Wow. But I want this programmatically!

Again, you're in business:

const $ = require('cash');
const out = $.ls('.', {l: true});

Not terse enough? How about this:

const out = $('ls -lah');

Not :sunglasses: enough? Try this:

require('cash') `
  cp -R ./src ./dest
  ls | grep *-spec.js | cat
  rm ./specResults.html 
`;

For even better programmatic Unix commands, check out ShellJS.

Learn more

Isn't this impossible to do in Node?

It was, before Vorpal.

Made with ❤ by dthree.

Love it? Give it a :star: or a tweet to help spread the word!

Notice

This is now an OPEN Open Source project. I am not able to invest a significant amount of time into maintaining Cash and so am looking for volunteers who would like to be active maintainers of the project. If you are interested, shoot me a note.

Introduction

Cash is a project working on a cross-platform implementation of the most used Unix-based commands in pure JavaScript and with no external dependencies.

The goal of Cash is to open up these commands to the massive JavaScript community for the first time, and to provide a cleaner, simpler and flexible alternative to applications like Cygwin for those wanting the Linux feel on Windows.

Cash was built with strict attention to nearly exact implementations and excellent test coverage of over 200 unit tests.

Supported commands

The following commands are currently implemented:

  • alias
  • cat
  • clear
  • cd
  • cp
  • echo
  • export
  • false
  • grep
  • head
  • kill
  • less
  • ls
  • mkdir
  • mv
  • pwd
  • rm
  • sort
  • source
  • tail
  • touch
  • true
  • unalias

Want more commands?

Configuration

Want to configure things to your heart's content? Just add your configurations in a .cashrc file (_cashrc also works, for Windows folk) and put that in your home directory. This supports anything you can do inside a cash command prompt (exporting environmental variables, aliases, etc.).

Contributing

We are currently looking for Core Team members who can push forward Cash at a rapid rate. Are you an awesome developer up to the challenge? Send me a ping.

Awesome contributors

  • @nfischer: Added source, export, true and false commands, among several other contributions.
  • @safinn: Added clear and tail commands.
  • @legien: Added head command.
  • @cspotcode: Implemented template literal execution.

FAQ

Why Cash?

In its very essence, Cash replaces the Windows CLI prompt (>) with the Unix one ($), the dollar symbol.

Cash was most fitting in this sense:

Ask and ye shall receive

> cash
$

Cash is also a play on the word bash, and is actually[1] a recursive acronym for Cash Shell.

Shout out to @aseemk for donating the name.

Doesn't ShellJS do this?

No.

For those who don't know, ShellJS is an awesome Node package that implements UNIX shell commands programatically in JavaScript. Check it out - really. While ShellJS was tremendously helpful in figuring out how to accomplish Cash, the two do not really conflict.

ShellJS gives the feel of UNIX commands in a code environment, but aims to implement the commands in a way that makes sense for a JavaScript library. This means that many commands return JavaScript objects, and some of the rougher and more dangerous edges of bash have been softened a bit.

For example, with cash:

$('ls'); // 'node_modules\n'

$('echo foo > foo.txt');

With ShellJS:

ls(); // ['node_modules'];

echo('foo').to('foo.txt');

Author: Dthree
Source Code: https://github.com/dthree/cash 
License: MIT license

#es6 #linux #javascript 

Cash: Cross-platform Linux Commands in ES6

Macroable: A Simple ES6 Class That Can Be Extended

Macroable

Extend class prototype in style 😎 

Base class for exposing external API to extend the class prototype in a more declarative way.

Table of contents

Traditional approach

class Foo {}
module.exports = Foo

Someone can extend it follows.

const Foo = require('./Foo')
Foo.prototype.greet = function () {
  return 'Hello!'
}

// or add getter as follow
Object.defineProperty(Foo.prototype, 'username', {
  get: function () {
    return 'virk'
  },
})

Using macroable

import { Macroable } from 'macroable'

class Foo extends Macroable {}

Foo.macros = {}
Foo.getters = {}

export default Foo
import Foo from './Foo'

Foo.macro('greet', function () {
  return 'Hello!'
})

Foo.getter('username', function () {
  return 'virk'
})

You can see the API is simpler and less verbose. However, there are couple of extra benefits of using Macroable.

Defining singleton getters

Singleton getters are evaluated only once and then cached value is returned.

Foo.getter('baseUrl', function () {
  return lazilyEvaluateAndReturnUrl()
}, true) 👈

Hydrating the class

Using the hydrate method, you can remove macros and getters added on a given class.

Foo.macro('greet', function (name) {
  return `Hello ${name}!`
})

Foo.getter('username', function () {
  return 'virk'
})

Foo.hydrate()  👈
Foo.greet // undefined
Foo.username // undefined

Author: Poppinss
Source Code: https://github.com/poppinss/macroable 
License: MIT license

#node #javascript #es6 

Macroable: A Simple ES6 Class That Can Be Extended

Tiniest Body Parser in The Universe. Built for Modern Node.js

Tiniest body parser in the universe. Built for modern Node.js.

Features

  • ⏩ built with async / await
  • 🛠 JSON / raw / urlencoded data support
  • 📦 tiny package size (728B)
  • 🔥 no dependencies
  • tinyhttp and Express support

Install

# pnpm
pnpm i milliparsec

# yarn
yarn add milliparsec

# npm
npm i milliparsec

Usage

Basic example

Use a middleware inside a server:

import { createServer } from 'http'
import { json } from 'milliparsec'

const server = createServer(async (req: ReqWithBody, res) => {
  await json()(req, res, (err) => void err && console.log(err))

  res.setHeader('Content-Type', 'application/json')

  res.end(JSON.stringify(req.body))
})

Web frameworks integration

tinyhttp

import { App } from '@tinyhttp/app'
import { urlencoded } from 'milliparsec'

new App()
  .use(urlencoded())
  .post('/', (req, res) => void res.send(req.body))
  .listen(3000, () => console.log(`Started on http://localhost:3000`))

API

raw(req, res, cb)

Minimal body parsing without any formatting.

text(req, res, cb)

Converts request body to string.

urlencoded(req, res, cb)

Parses request body using new URLSearchParams.

json(req, res, cb)

Parses request body using JSON.parse.

custom(fn)(req, res, cb)

Custom function for parsec.

// curl -d "this text must be uppercased" localhost
await custom(
  req,
  (d) => d.toUpperCase(),
  (err) => {}
)
res.end(req.body) // "THIS TEXT MUST BE UPPERCASED"

What is "parsec"?

The parsec is a unit of length used to measure large distances to astronomical objects outside the Solar System.

Check out deno-libs/parsec for Deno port.

Author: Tinyhttp
Source Code: https://github.com/tinyhttp/milliparsec 
License: MIT license

#node #javascript #http #es6 

Tiniest Body Parser in The Universe. Built for Modern Node.js

Maple.js: A React Webcomponents Based Framework Mixing ES6

Maple is a seamless module that allows you to organise your React project in terms of webcomponents — with HTML Imports, Shadow DOM, and Custom Elements — allowing you to implement any Flux architecture you choose, and then compile with Mapleify for production.

Screenshot

Getting Started

:maple_leaf: Watch "Getting Started with Maple": https://vimeo.com/128387987 (Previous)

:gem: Install all dependencies and start server using npm start.

Given the typical Flux architecture where components reside in their respective components directory, we continue that trend in Maple, where one component can register one or many custom elements – but each HTML document can only have one template element.

Within the directory my-app/components we create our component's index that will be imported — date-time.html — which will import its associated JavaScript and CSS documents:

<template>
    <script type="text/jsx" src="date-time.js"></script>
    <script type="text/javascript" src="../../../vendor/moment/moment.js"></script>
    <link rel="stylesheet" type="text/css" href="date-time.css" />
</template>

Note: When we import the date-time.js file we use the local path, which Maple.js understands as being a part of the module – whereas our third-party module — moment.js — resides outside of the component's directory and is therefore imported into the window scope.

Within our CSS file, we can be as loose as we like, because the date-time.js component will be imported under its own shadow boundary, preventing the styles from bleeding over into other components — even components that are children of our component.

We next need to add some standard ES6 React code to our date-time.js to make it return a date and time when rendered:

export default class MyDateTime extends React.Component {

    render() {
        let dateTime = moment().format(this.props.format || 'YYYY-MM-DD');
        return <time>{dateTime}</time>
    }

}

Note: You could use the React.createElement('datetime', null, dateTime) approach as well – the System.import we use recognises when it's a JSX file and will transpile it automatically for you.

By looking at the above React component, we can immediately deduce that the eventual custom element will be called my-date-time. For those eagle-eyed individuals amongst us, you'll have noticed we use this.props.format to specify the date/time format – and this is something we'll pass into our component when adding the custom element to the DOM.

Next all we need to do is add a little CSS to our date-time.css document:

time {
    color: rebeccapurple;
    font-family: Arial, Tahoma, Helvetica, sans-serif;
}

And finally import the component into our main index.html document that includes the maple.js and react.js imports:

<link rel="import" type="text/html" href="my-app/components/time-date/index.html" />

Note: You may have noticed that the component's directory name is largely irrelevant – and it is, in most cases. However, there are certain circumstances where the component's directory matters – such as when registering a Worker — In this case Maple provides the component directory as this.props.path.

Once the HTML document has been imported, Maple will register our custom element and it will be then usable in our application – although don't forget that we should pass in the optional format attribute to override YYYY-MM-DD:

<my-date-time data-format="YYYY-MM-DD HH:mm"></my-date-time>

Note: In the above example we use data-format, whereas our React component expects format — you'll be glad to know that in these cases, Maple strips the data- segment from the attribute, which allows you to write perfectly valid HTML5 syntax.

Component Path

From within your React component, use the this.props.path.getRelativePath() to get the path of the current component – with this information, you can easily register Workers and other relatively stored documents:

let name   = 'MyWebWorker.js',
    path   = `${this.props.path.getRelativePath()}/${name}`,
    worker = new Worker(path);

Ignore Import

Importing a HTML file may not require Maple at all, and therefore if the imports were left to be processed by Maple this would be a waste of resources – as no components would be contained within the import. For these cases you can add the data-ignore attribute to the HTML import, and Maple will leave them unprocessed:

<link rel="import" type="text/html" href="example.html" data-ignore />

Multiple Elements

Each HTML document can have exactly one template element registering components. In cases where you want to register multiple components, you must split them into their individual HTML documents for developers to import separately. For instance, a DateTime component could yield date-time-gmt, date-time-bst, etc... Each element can have its own associated CSS documents as well. There are two approaches for this:

  1. Create two HTML documents: index-gmt.html and index-bst.html and require them to be imported separately;
  2. Create one HTML import with one template node and import both JS documents with a shared CSS document:
<template>
    <script type="text/javascript" src="datetime-gmt.js"></script>
    <script type="text/javascript" src="datetime-bst.js"></script>
    <link rel="stylesheet" type="text/css" href="shared.css" />
</template>

Choosing between the two approaches should be evident – if you want to apply custom CSS documents to each component individually — datetime-gmt.css to one, and datetime-bst.css to the other — then you should have two HTML documents. Otherwise if the two are directly related, and share the same CSS and JS documents, then they can be kept together in one HTML document.

JSX Compilation

In development environments it is often useful to compile JSX documents — Maple supports JSX compilation. All you have to do is import JSX the usual JSX way using the text/jsx type:

<template>
    <script type="text/jsx" src="my-jsx-document.js"></script>
</template>

Note: When using Mapleify to render your app – Mapleify merely changes the type of your script elements from text/jsx to text/javascript and changes the extensions from .jsx to .js (pre v1.2.0 when JSX files were included with JSX extensions) – it's left entirely up to the developer to write their Gulp/Grunt scripts to convert their JSX — and SASS — documents prior to Mapleify compilation.

Also Note: Since the release of v1.2.0 JSX files must have a JS extension – also JSX files will import just fine using a text/javascript type, too.

Nested Shadow Boundaries

As Maple uses Custom Elements to create the components, it's straightforward to have components within components – you only need to place your Custom Element node into your React component:

render() {
    return <li><date-time data-unix={model.date}></date-time></li>
}

SASS Transpiling

:maple_leaf: Watch "SASS to CSS": https://vimeo.com/128343626

In a development environment Maple supports transpiling SASS documents to CSS documents – for production you should use your build tool to transpile SASS to CSS documents.

Maple uses Sass.js and can be installed separately:

bower install sass.js -D

Once you have included sass.js all documents that are included with the type text/scss will be automatically transpiled for you to CSS before being appended to the shadow boundary:

<link type="text/scss" href="default.scss" />

Resolved Components (FOUC)

:maple_leaf: Watch "Preventing FOUC": https://vimeo.com/128343604

Maple uses the same mechanism as Polymer when it comes to preventing FOUC. Place the unresolved attribute on each element, and then once they're upgraded by Maple, the unresolved attribute will be replaced with the resolved attribute:

<date-time unresolved></date-time>

With the following styles the date-time element will fade in gradually once upgraded:

date-time {
    opacity: 0;
    display: block;
    transition: opacity 3s;
}

date-time[resolved] {
    opacity: 1;
}

Mutation Observer

:maple_leaf: Watch "Mutation Observer": https://vimeo.com/128588608

Maple uses the MutationObserver to listen for changes to the document.head element – if new elements are added to the node, then Maple will eagerly attempt to resolve the HTML imports and load them dynamically.

For components to be processed by the mutation observer, link elements must:

  • Be a child of the document.head element;
  • Pass the utility.isHTMLImport method;

The utility.isHTMLImport method checks for the following to determine whether the newly added element is a valid link import:

  • Is an instance of HTMLLinkElement;
  • rel attribute resolves to string import;
  • Has the href attribute defined;
  • type attribute resolves to string text/html;

Once the element has passed the aforementioned check, Maple will load in the component and it will be ready to use. As an example, let's dynamically load our DateTime component from the first tutorial:

var linkElement = document.createElement('link');
linkElement.setAttribute('href', 'app/components/todo-form/index.html');
linkElement.setAttribute('type', 'text/html');
linkElement.setAttribute('rel', 'import');
document.head.appendChild(linkElement);

It's worth noting that the above code contains a fair amount of boilerplate code, which is why you'll likely want to have a wrapper function for this. After the linkElement has been appended to the document.head element, Maple will resolve the HTML import via the MutationObserver.

Extending Native Elements

:warning: Not yet supported – merged and pending release.

:maple_leaf: Watch "Extending Elements": https://vimeo.com/128589729

By default all of your Maple components will be simple elements. For example, the class DateTime object will create an element called date-time – in cases where you'd like the element to be specialised — such as extending the HTMLButtonElement then you need to modify the object's name:

export default class DateTime_Button {}

In the above case the element will still be registered as date-time – but now the date-time element will extend HTMLButtonElement.prototype:

<button is="date-time">
    DateTime Button!
</button>

Mapleify (Vulcanization)

For development purposes the HTML Imports are an acceptable design implementation – however when pushing to production — as you do with Polymer — you'll want to minify and concatenate your resources. In Polymer you would use vulcanizeMaple utilises vulcanize to create Mapleify which compiles your HTML document.

You can install Mapleify globally with npm: npm install mapleify -g – it can then be used from your terminal:

mapleify -i index.html (default renders to mapleify.html – change with -o rendered.html)

Browser Support

Chrome Firefox Opera Safari Safari

Note: Example has also been tested in IE11 where it seems to be functioning well.

Maple also comes distributed with a dist/maple-polyfill.js file that includes all necessary polyfills for the widest possible support in modern browsers.

Example Todo

We have a typical todo example on Heroku which uses Maple along with the Alt.js Flux library. Everything should look familiar to a seasoned React.js developer – with the customary stores and actions – where the codebase differs is in the components directory, where each of the three components are written in ES6 and exported using export default.

Selectors

It's crucial to know how Maple traverses the DOM to find your CSS/SASS and JS documents. Maple attempts to adhere to the HTML5 standard – and therefore if you notice something amiss, please open an issue!

  • External CSS: Must have rel="stylesheet" – all other attributes optional;
  • Inline CSS: Optional type="text/css" attribute;
  • HTML Imports: Must have rel="import" – all other attributes optional;
  • Inline Templates: Must have a ref attribute – automated by Mapleify;
  • External JS: Optional type="text/css" attribute – matches JSX with type="text/jsx";

Namespaces

In some cases it may be desirable to prepend a namespace to all custom elements – especially in the case where you're loaded a third-party import and are unable to touch their custom elements directly. In these instances Maple allows you to specify a namespace when importing the document:

<link rel="import" href="app/components/date-time/index.html" data-namespace="x" />

By specifying the data-namespace attribute, you effectively prepend x to all custom elements imported by that document. Therefore if date-time defined a date-time element, with the data-namespace attribute as x the element would now be x-date-time which helps to prevent naming conflicts.

Testing

Maple uses Polymer's wct testing tool – which relies on the Chai assertion library.

  • npm install
  • bower install
  • gulp test

Optionally you may also invoke the wct testing yourself by issuing the wct command in your terminal.



Author: Wildhoney
Source Code: https://github.com/Wildhoney/Maple.js 
License: MIT license

#react #javascript #es6 #html 

Maple.js: A React Webcomponents Based Framework Mixing ES6
许 志强

许 志强

1652678160

【JS 新手入門】 為什麽JS有分ES6、ES7?認識ECMAScript

我們常聽到ES6、ES7其實是指版本上的不同?到底為什麽要這樣分呢?初學者建議一開始要學哪一種?來聽聽老師的講解吧^^

什么是 ECMAScript 6

ECMAScript 6.0(以下简称 ES 6)是 JavaScript 语言的下一代标准,已经在2015年6月正式发布了。它的目标,是使得 JavaScript 语言可以用来编写复杂的大型应用程序,成为企业级开发语言。

标准的制定者有计划,以后每年发布一次标准,使用年份作为版本。因为 ES 6 的第一个版本是在2015年发布的,所以又称 ECMAScript 2015(简称 ES 2015)。

ECMAScript 和 JavaScript 的关系

1996年11月,JavaScript 的创造者 Netscape 公司,决定将 JavaScript 提交给国际标准化组织 ECMA,希望这种语言能够成为国际标准。次年,ECMA 发布262号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为 ECMAScript,这个版本就是1.0版。

该标准从一开始就是针对 JavaScript 语言制定的,但是之所以不叫 JavaScript,有两个原因。一是商标,Java 是 Sun 公司的商标,根据授权协议,只有 Netscape 公司可以合法地使用 JavaScript 这个名字,且 JavaScript 本身也已经被 Netscape 公司注册为商标。二是想体现这门语言的制定者是 ECMA,不是 Netscape,这样有利于保证这门语言的开放性和中立性。

因此,ECMAScript 和 JavaScript 的关系是,前者是后者的规格,后者是前者的一种实现(另外的 ECMAScript 方言还有 Jscript 和 ActionScript)。日常场合,这两个词是可以互换的。


#javascript  #es6 #css #jquery 

【JS 新手入門】 為什麽JS有分ES6、ES7?認識ECMAScript