npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

estime

v1.3.0

Published

estime = ecmascript + runtime, in javascipt(es5) environment

Downloads

20

Readme

estime

estime = ecmascript + runtime, in javascipt(es5) environment

基于 TypeScript 编写的 JavaScript 解释器,运行于es的环境,且原生支持es6\jsx等众多常用的新特性。独立、安全。

初版fork于eval5,目标是原生支持es2017(非严格)语法和JSX且修改bug,持续开发中,进度请查看最后的todoList

使用场景

  • 不支持eval Function的 JavaScript 运行环境:如 微信小程序。
  • 支持eval的Javascript环境,但是又担心eval的安全性问题。
  • 需要代码动态更新的场景。例如你的React应用需要热更新组件;你的规则系统需要动态下发规则脚本等等。
  • 研究/学习用

功能演示

安装

npm i estime -S

使用

import { Interpreter } from "estime";

const interpreter = new Interpreter({
  console,
  rt: (val) => (res = val)
});

try {
  let res;
  interpreter.evaluate(`
    class Test {
      name = 'default_test';
      setName = (name) => {
        this.name = name
      }
    }
    let t = new Test
    t.setName('hello')
    console.info(t.name)
    rt(t.name)
  `);
  console.info('the result is ', res)
} catch (e) {
	console.log(e);
}

参数

interface Options {
	// 根作用域,只读
	rootContext?: {} | null;
	globalContextInFunction?: any;
}

Example

import { Interpreter } from "estime";

const ctx = {};
const interpreter = new Interpreter(ctx, {
  rootContext: window,
});

interpreter.evaluate(`
  a = 100;
  console.log(a); // 100
`);

window.a; //undefined

Interpreter静态属性

global

默认值: {}

设置默认的全局作用域

Interpreter.global = window;
const interpreter = new Interpreter();
interpreter.evaluate('alert("hello estime")');

globalContextInFunction

默认值: undefined

estime 不支持 use strict 严格模式, 在非严格下的函数中this默认指向的是全局作用域,但在estime中是undefined, 你可以通过globalContextInFunction来设置默认指向。

Interpreter.globalContextInFunction = window;
const interpreter = new Interpreter();
interpreter.evaluate('alert("hello estime")');

ES2015 支持

import {Interpreter} from '../src/interpreter/main'

let inter = new Interpreter(null)

let res = inter.evaluate(`
let a = (function(){
    if(true){
        if(true){
            try{
                let a = b;
                let b = 123;
                var result = 456;
            }catch(e){
				// e is Cannot access 'b' before initialization
                result = 123;
            }
        }
    }
    return result
})()
a
`);
console.info(res)  // 123

JSX支持

其中不支持JSXFragments、JSXNamespacedName和JSXSpreadChild。JSX标准参考fb的jsx specification acorn-jsx不支持JSXSpreadChild,且用的比较少。例如下面两种content的用法,效果是一样的,为什么要我要去用spread呢?暂且不支持吧。

使用estime完成React组件动态更新的例子,非常灵活

let content = [1,2,3]
let t = <div>
  {content}
  {...content}
</div>

jsx使用例子:

let code = `
let props = {
   style: { border: '1px solid #333', color: 'red', borderRadius: 3, padding: 10, margin: 10 }
}

class Panel extends React.Component{
   render(){
       return <div {...props}>this is other Component: Panel</div>
   }
}
class TT extends React.Component{
  render(){
    return <div {...props}>
      hello world
      <input disabled style={{display: 'block', width: 300,}} />
      <Panel />
    </div>
  }
}
__rt(TT)
`
class Test {
  getCpt(code){
    let interRes;
    let inter = new Interpreter({
      __rt: val => (interRes = val),
      console,
      React: React,
    })
    try{
      inter.evaluate(code)
    }catch(e){
      console.info(e)
      return e.message
    }
    return interRes
  }
  render(){
    let C = this.state.C
    return <div>
      <button onClick={_=>{
        let Cpt = this.getCpt(code)
        this.setState({
          C: Cpt
        })
      }}>点击生成组件</button>
      {C && <div><C/></div>}
    </div>
  }
}

效果如下:

相关

异步队列方案

要在沙箱内部支持异步方法,就必须用js去模拟整个task的执行流程,task分为micro task和macro task。即我们说的大队和小队。Promise入的是小队,setTimeout入的是大队。这里是难点,这一套机制是整个异步流程的基石。

那么,怎么实现大小队呢?参考现有的promise pollyfill库,promise.js用的是asapasap底层是process.nextTick降级到queueMicrotask再降级到MutationObserver最后降级到setTimeout。那么对于一个沙箱环境,我们是否有必要做得这么复杂呢?

首先明确一点,在浏览器环境,用户可以任意编写方法调用setTimeout或是queueMicrotask或是Promise.resolve等等,浏览器一般并没有限制。所以,当在浏览器实现一个小队的polyfill时候,就需要判断各种各样的api接口是否可用,然后处理各种降级,为的就是当用户调用你的fakeMicroTask放入的函数必定比他自己调用setTimeout放入的函数后执行。

那么,如果是沙箱环境,任何函数的实际执行都是沙箱决定的,沙箱完全可以只提供一个虚拟的“队列”,无论是setTimeout还是queueMicrotask还是Promise.resolve都放入同一个虚拟队列中,只是他们优先级不一样而已。那么,只要我们沙箱外部环境拥有setTimeout的能力(这几乎是100%兼容的),我们就能够提供这样的虚拟队列,保证沙箱环境的各种不同优先级的异步方法执行;且这样做还有个好处,就是沙箱中的异步方法,永远都是优先级最低的setTimeout,不和外部环境抢小队的时间片。

实现虚拟的大小队的标准可以参照event-loop-processing-model

generator相关实现方法

generator的实现也是难点,但其功能又如此重要(异步方法语法糖的基础),不得不支持。目前正在纠结中,将generator的定义转换成es5可执行的同等函数,其工作量不亚于再写一个js解释器。有现成的npm包比如regenerator可以将generator的代码转换成es5的形式,但其包体大小压缩有都有足足1M,我是肯定不会用的。typescript的源码中也带有转换generator,但由于依赖挺多的,拆分出来的成本较大。经过一段时间的源码阅读,发现regenerator实际是基于babel-plugin写的一个ast替换插件,整体核心部分大致有2000+行代码,加上运行时600行代码,比较合理。不过regenerator基于babel-plugin,用到了babylon的语法分析能力,也基于babel-types和babel的travel能力,这部分代码庞大,需要去掉自己写;且babylon的语法分析结果和acorn.js的语法分析声明的都是遵守estree,不过两者最终输出的ast结构还是有差异的,estime基于acorn做语义分析,这部分适配工作也需要自己做。

整体思路很简单,如果遇到了async或generator函数,先进行ast的转译,然后再进行接下来的编译闭包工作。对ast的转译工作单独放在了这个库里:estime-resync

todoList

es2015\es2017等等申明,个人感觉是非严格的es规范支持声明。es的规范会经过不同stage的提案状态,有些特性还在stage-1等就已经放出来开始广泛使用了。所以对于es2015,你会看到有“对象解构”,但是实际上在2015年的时候,它还不是stage-4。我看acorn.js在es2018才支持解构,但是babel的文档上,es2015就已经包含解构了,这样的差异还真不好细究清楚,且深究也没有意义。所以,我没有局限在2015还是2017上,关注的是特性,需要支持的特性下面的todolist都会列出来。

相关特性可以看这里,并不一定全部实现。但常用的都会实现的。

  • [x] 块级作用域
    • [x] let
    • [x] const
  • [x] Class
    • [x] 基础声明
    • [x] extends
    • [x] class fields
    • [x] static property
  • [x] 箭头函数
    • [x] 基础执行支持
    • [x] context绑定
  • [x] 解构
    • [x] 对象解构
    • [x] 数组解构
    • [x] 函数实参解构
  • [x] Rest element
    • [x] ObjectPattern
    • [x] ArrayPattern
    • [x] 函数形参rest
  • [x] Map + Set + WeakMap + WeakSet 由外部提供支持,沙箱不做特殊支持
  • [x] for-of
  • [x] Template Strings
  • [x] Computed property
  • [x] Symbols
  • [x] Array新增方法等
    • [x] Array.from
    • [x] Array.of
    • [x] Array.prototype.entries
    • [x] Array.prototype.values
    • [x] Array.prototype.keys
    • [x] Array.prototype.reverse
    • [x] Array.prototype.find
    • [x] Array.prototype.fill
    • [x] Array.prototype.lastIndexOf
    • [x] Array.prototype.findIndex
    • [x] Array.prototype.copyWithin
    • [x] Array.prototype.includes
    • [x] Array.prototype.flat
    • [x] Array.prototype.flatMap
    • [x] Array.prototype.reduceRight
  • [x] 异步函数
    • [x] 虚拟大小队列core
    • [ ] 虚拟大小队列的自动销毁
    • [x] setTimeout
    • [x] setInterval 不支持,容易造成timer泄露,我鼓励自己用setTimeout来实现interval功能
    • [x] Promise
    • [x] queueMicrotask
  • [ ] Generators
  • [ ] async/await
    • [X] Async generator functions 不支持
  • [x] JSX支持,其中不支持JSXFragments、JSXNamespacedName和JSXSpreadChild。JSX标准参考fb的jsx specification
    • [x] JSXElement
    • [x] JSXIdentifier for React IntrinsicElement
    • [x] SelfClosing
    • [x] JSXExpressionContainer
    • [x] JSXText
  • [ ] 抽离acorn.js的依赖。为网络传输提供可靠安全的基础。
    • [ ] AST的压缩(可能是二进制)表示形式
    • [ ] 源码打包器
    • [ ] 压缩AST解释器runtime

License

Mozilla Public License Version 2.0