├── .github
└── workflows
│ └── npm-publish.yml
├── .gitignore
├── .prettierrc
├── LICENSE
├── README.md
├── demos
└── taro-demo
│ ├── .eslintrc.js
│ ├── .prettierrc
│ ├── README.md
│ ├── babel.config.js
│ ├── config
│ ├── dev.js
│ ├── index.js
│ └── prod.js
│ ├── global.d.ts
│ ├── package.json
│ ├── project.config.json
│ ├── project.tt.json
│ ├── src
│ ├── app.config.ts
│ ├── app.less
│ ├── app.tsx
│ ├── index.html
│ └── pages
│ │ └── index
│ │ ├── index.config.ts
│ │ ├── index.less
│ │ └── index.tsx
│ ├── tsconfig.json
│ └── yarn.lock
├── docs
└── imgs
│ ├── code-demo.jpeg
│ └── demo.jpeg
├── jest.config.js
├── package.json
├── rollup.config.js
├── src
├── index.ts
├── js-interpreter
│ ├── acorn.js
│ └── interpreter.js
└── react-interpreter
│ ├── ReactInterpreter.ts
│ ├── constants.ts
│ ├── createAsyncSwitcher.test.ts
│ ├── createAsyncSwitcher.ts
│ ├── injectGlobalObject.ts
│ ├── transformComponent.test.tsx
│ └── transformComponent.ts
├── tsconfig.json
└── yarn.lock
/.github/workflows/npm-publish.yml:
--------------------------------------------------------------------------------
1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
3 |
4 | name: Node.js Package
5 |
6 | on:
7 | release:
8 | types: [created]
9 |
10 | jobs:
11 |
12 | publish-npm:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v2
16 | - uses: actions/setup-node@v2
17 | with:
18 | node-version: 16
19 | registry-url: https://registry.npmjs.org/
20 | - run: yarn
21 | - run: npm run release
22 | env:
23 | NODE_AUTH_TOKEN: ${{secrets.npm_token}}
24 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 |
9 | # Diagnostic reports (https://nodejs.org/api/report.html)
10 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
11 |
12 | # Runtime data
13 | pids
14 | *.pid
15 | *.seed
16 | *.pid.lock
17 |
18 | # Directory for instrumented libs generated by jscoverage/JSCover
19 | lib-cov
20 |
21 | # Coverage directory used by tools like istanbul
22 | coverage
23 | *.lcov
24 |
25 | # nyc test coverage
26 | .nyc_output
27 |
28 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
29 | .grunt
30 |
31 | # Bower dependency directory (https://bower.io/)
32 | bower_components
33 |
34 | # node-waf configuration
35 | .lock-wscript
36 |
37 | # Compiled binary addons (https://nodejs.org/api/addons.html)
38 | build/Release
39 |
40 | # Dependency directories
41 | node_modules/
42 | jspm_packages/
43 |
44 | # TypeScript v1 declaration files
45 | typings/
46 |
47 | # TypeScript cache
48 | *.tsbuildinfo
49 |
50 | # Optional npm cache directory
51 | .npm
52 |
53 | # Optional eslint cache
54 | .eslintcache
55 |
56 | # Microbundle cache
57 | .rpt2_cache/
58 | .rts2_cache_cjs/
59 | .rts2_cache_es/
60 | .rts2_cache_umd/
61 |
62 | # Optional REPL history
63 | .node_repl_history
64 |
65 | # Output of 'npm pack'
66 | *.tgz
67 |
68 | # Yarn Integrity file
69 | .yarn-integrity
70 |
71 | # dotenv environment variables file
72 | .env
73 | .env.test
74 |
75 | # parcel-bundler cache (https://parceljs.org/)
76 | .cache
77 |
78 | # Next.js build output
79 | .next
80 |
81 | # Nuxt.js build / generate output
82 | .nuxt
83 | dist
84 |
85 | # Gatsby files
86 | .cache/
87 | # Comment in the public line in if your project uses Gatsby and *not* Next.js
88 | # https://nextjs.org/blog/next-9-1#public-directory-support
89 | # public
90 |
91 | # vuepress build output
92 | .vuepress/dist
93 |
94 | # Serverless directories
95 | .serverless/
96 |
97 | # FuseBox cache
98 | .fusebox/
99 |
100 | # DynamoDB Local files
101 | .dynamodb/
102 |
103 | # TernJS port file
104 | .tern-port
105 |
106 | dist
107 | lib
108 | es
109 | types
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "eslintIntegration": true,
3 | "tabWidth": 4,
4 | "singleQuote": true,
5 | "semi": false,
6 | "printWidth": 120,
7 | "bracketSpacing": true,
8 | "jsxBracketSameLine": false,
9 | "jsxSingleQuote": true,
10 | "trailingComma": "es5"
11 | }
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 wuchangming
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-interpreter
2 | **⚠️⚠️⚠️ 该方案不再维护,改为 [mini-hot](https://github.com/mini-hot/mini-hot) 中维护**
3 |
4 | React 沙盒 📦,可理解为 React 版的 `eval()` 。该沙盒运行机制可使基于 React 实现的小程序框架「如 Taro3 等」拥有 🚀 **热更新**能力。
5 |
6 |
7 |
8 |
9 | ## 安装
10 |
11 | ```
12 | npm install react-interpreter --save
13 | ```
14 |
15 | 或者
16 |
17 | ```
18 | yarn add react-interpreter --save
19 | ```
20 |
21 | ## API
22 |
23 | ### `ReactInterpreter` - React 沙盒组件
24 |
25 | ---
26 |
27 | - ### **Props**
28 |
29 | - #### `code` -- React 沙盒运行的代码字符串
30 |
31 | ⚠️ `PS: React 沙盒组件运行的字符串代码需使用 es5 编写的函数组件,不支持 hooks、class 组件。不直接支持 jsx 写法,可以先通过` [**babel 进行转换**](https://babeljs.io/repl/#?browsers=defaults&build=&builtIns=false&corejs=3.6&spec=false&loose=false&code_lz=Q&debug=false&forceAllTransforms=false&shippedProposals=false&circleciRepo=&evaluate=false&fileSize=false&timeTravel=false&sourceType=module&lineWrap=true&presets=env%2Creact%2Cstage-2&prettier=true&targets=&version=7.17.2&externalPlugins=&assumptions=%7B%7D)
32 |
33 | ```ts
34 | import { ReactInterpreter } from 'react-interpreter'
35 | import { View, Text } from '@tarojs/components'
36 | /*
37 | 【Babel 编译前组件代码】
38 | */
39 | /*
40 | 注意:这个组件名命名只要不和注入的组件重名就行,没有特别要求
41 | function MyComp() {
42 | return (
43 |
52 | Hello World !
53 |
54 | )
55 | }
56 | */
57 | /*
58 | 【Babel 编译后组件代码 string】
59 | */
60 | const codeString = `function MyComp() {
61 | return React.createElement(
62 | View,
63 | {
64 | style: {
65 | backgroundColor: '#00C28E',
66 | height: '100vh',
67 | display: 'flex',
68 | alignItems: 'center',
69 | justifyContent: 'center',
70 | },
71 | },
72 | React.createElement(Text, null, 'Hello World !')
73 | )
74 | }`
75 | const MyComp = () => (
76 |
83 | )
84 | ```
85 |
86 | - 效果图
87 |
88 |
89 |
90 | - #### `globalObject` -- 需要注入沙盒中的全局变量
91 |
92 | ```ts
93 | globalObject = {
94 | wx, // 注入 wx 全局变量
95 | console, // 注入 console 控制台
96 | }
97 | ```
98 |
99 | - #### `componentMap` -- 需要注入沙盒中的 React 组件
100 |
101 | ```ts
102 | import { View } from '@tarojs/components'
103 | componentMap = {
104 | View,
105 | }
106 | ```
107 |
108 | - #### `globalObjectComplexPropLevel` -- 全局变量复杂属性最大层级
109 |
110 | `默认值:3`
111 |
112 | `设置被注入的全局变量的复杂属性最大层级。为了保证转化效率,大于该层级的任何不能 JSON.stringify 的内容都会被丢弃掉「如 function 和出现循环引用的 object 等」。`
113 |
114 | - #### `沙盒组件 props 传值方式`
115 |
116 | `除了 ReactInterpreter API 外的其他 props 都会被直接透传到沙盒内的组件`
117 |
118 | ```ts
119 | const codeString = `
120 | function MyComp(props) {
121 | return /*#__PURE__*/ React.createElement(
122 | Button,
123 | {
124 | onClick: props.onClickMe
125 | },
126 | "I am a button -- ",
127 | props.btnName
128 | );
129 | }
130 | `
131 |
132 | const MyComp = () => (
133 | {
141 | console.log('我被点击了!')
142 | }}
143 | >
144 | )
145 | ```
146 |
147 | ### `JSInterpreter` - JS 沙盒
148 |
149 | ---
150 |
151 | 如果只需要执行 JS ,可直接使用 JSInterpreter
152 |
153 | - ### 基本用法
154 |
155 | ```ts
156 | import { JSInterpreter } from 'react-interpreter'
157 |
158 | const myInterpreter = new JSInterpreter('6 * 7')
159 | myInterpreter.run()
160 | console.log(myInterpreter.value)
161 | ```
162 |
163 | JSInterpreter 代码基本都是使用的 [JS-Interpreter](https://github.com/NeilFraser/JS-Interpreter) 项目,只做了对微信小程序相关 bug 的修复,所以详细文档可直接参考 JS-Interpreter 文档: [https://neil.fraser.name/software/JS-Interpreter/docs.html](https://neil.fraser.name/software/JS-Interpreter/docs.html)
164 |
165 | ## 实例 Demo
166 |
167 | - ### Taro3 中用法示例 [查看 Demo 项目](./demos/taro-demo/)
168 |
169 | ## 灵感来源
170 |
171 | - [JS-Interpreter](https://github.com/NeilFraser/JS-Interpreter)
172 | - [jsjs](https://github.com/bramblex/jsjs)
173 |
--------------------------------------------------------------------------------
/demos/taro-demo/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | // "extends": ["taro/react"],
3 | "rules": {
4 | "react/jsx-uses-react": "off",
5 | "react/react-in-jsx-scope": "off"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/demos/taro-demo/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "eslintIntegration": true,
3 | "tabWidth": 4,
4 | "singleQuote": true,
5 | "semi": false,
6 | "printWidth": 120,
7 | "bracketSpacing": true,
8 | "jsxBracketSameLine": false,
9 | "jsxSingleQuote": true,
10 | "trailingComma": "es5"
11 | }
12 |
--------------------------------------------------------------------------------
/demos/taro-demo/README.md:
--------------------------------------------------------------------------------
1 | # react-interpreter 的 Taro Demo
2 |
3 | ## 启动命令
4 | step1:
5 | ```
6 | npm i
7 | ```
8 | step2:
9 |
10 | ```
11 | npm run dev:weapp
12 | ```
--------------------------------------------------------------------------------
/demos/taro-demo/babel.config.js:
--------------------------------------------------------------------------------
1 | // babel-preset-taro 更多选项和默认值:
2 | // https://github.com/NervJS/taro/blob/next/packages/babel-preset-taro/README.md
3 | module.exports = {
4 | presets: [
5 | ['taro', {
6 | framework: 'react',
7 | ts: true
8 | }]
9 | ]
10 | }
11 |
--------------------------------------------------------------------------------
/demos/taro-demo/config/dev.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | NODE_ENV: '"development"'
4 | },
5 | defineConstants: {
6 | },
7 | mini: {},
8 | h5: {}
9 | }
10 |
--------------------------------------------------------------------------------
/demos/taro-demo/config/index.js:
--------------------------------------------------------------------------------
1 | const config = {
2 | projectName: 'taro-demo',
3 | date: '2022-2-9',
4 | designWidth: 750,
5 | deviceRatio: {
6 | 640: 2.34 / 2,
7 | 750: 1,
8 | 828: 1.81 / 2
9 | },
10 | sourceRoot: 'src',
11 | outputRoot: 'dist',
12 | plugins: [],
13 | defineConstants: {
14 | },
15 | copy: {
16 | patterns: [
17 | ],
18 | options: {
19 | }
20 | },
21 | framework: 'react',
22 | mini: {
23 | postcss: {
24 | pxtransform: {
25 | enable: true,
26 | config: {
27 |
28 | }
29 | },
30 | url: {
31 | enable: true,
32 | config: {
33 | limit: 1024 // 设定转换尺寸上限
34 | }
35 | },
36 | cssModules: {
37 | enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
38 | config: {
39 | namingPattern: 'module', // 转换模式,取值为 global/module
40 | generateScopedName: '[name]__[local]___[hash:base64:5]'
41 | }
42 | }
43 | }
44 | },
45 | h5: {
46 | publicPath: '/',
47 | staticDirectory: 'static',
48 | postcss: {
49 | autoprefixer: {
50 | enable: true,
51 | config: {
52 | }
53 | },
54 | cssModules: {
55 | enable: false, // 默认为 false,如需使用 css modules 功能,则设为 true
56 | config: {
57 | namingPattern: 'module', // 转换模式,取值为 global/module
58 | generateScopedName: '[name]__[local]___[hash:base64:5]'
59 | }
60 | }
61 | }
62 | }
63 | }
64 |
65 | module.exports = function (merge) {
66 | if (process.env.NODE_ENV === 'development') {
67 | return merge({}, config, require('./dev'))
68 | }
69 | return merge({}, config, require('./prod'))
70 | }
71 |
--------------------------------------------------------------------------------
/demos/taro-demo/config/prod.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | env: {
3 | NODE_ENV: '"production"'
4 | },
5 | defineConstants: {
6 | },
7 | mini: {},
8 | h5: {
9 | /**
10 | * WebpackChain 插件配置
11 | * @docs https://github.com/neutrinojs/webpack-chain
12 | */
13 | // webpackChain (chain) {
14 | // /**
15 | // * 如果 h5 端编译后体积过大,可以使用 webpack-bundle-analyzer 插件对打包体积进行分析。
16 | // * @docs https://github.com/webpack-contrib/webpack-bundle-analyzer
17 | // */
18 | // chain.plugin('analyzer')
19 | // .use(require('webpack-bundle-analyzer').BundleAnalyzerPlugin, [])
20 |
21 | // /**
22 | // * 如果 h5 端首屏加载时间过长,可以使用 prerender-spa-plugin 插件预加载首页。
23 | // * @docs https://github.com/chrisvfritz/prerender-spa-plugin
24 | // */
25 | // if (process.env.TARO_ENV === 'h5') {
26 | // const path = require('path')
27 | // const Prerender = require('prerender-spa-plugin')
28 | // const staticDir = path.join(__dirname, '..', 'dist')
29 | // chain
30 | // .plugin('prerender')
31 | // .use(new Prerender({
32 | // staticDir,
33 | // routes: [ '/pages/index/index' ],
34 | // postProcess: (context) => ({ ...context, outputPath: path.join(staticDir, 'index.html') })
35 | // }))
36 | // }
37 | // }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/demos/taro-demo/global.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare module "*.png";
4 | declare module "*.gif";
5 | declare module "*.jpg";
6 | declare module "*.jpeg";
7 | declare module "*.svg";
8 | declare module "*.css";
9 | declare module "*.less";
10 | declare module "*.scss";
11 | declare module "*.sass";
12 | declare module "*.styl";
13 |
14 | declare namespace JSX {
15 | interface IntrinsicElements {
16 | 'import': React.DetailedHTMLProps, HTMLEmbedElement>
17 | }
18 | }
19 |
20 | // @ts-ignore
21 | declare const process: {
22 | env: {
23 | TARO_ENV: 'weapp' | 'swan' | 'alipay' | 'h5' | 'rn' | 'tt' | 'quickapp' | 'qq' | 'jd';
24 | [key: string]: any;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/demos/taro-demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "taro-demo",
3 | "version": "1.0.0",
4 | "private": true,
5 | "description": "react-interpreter 的 Taro Demo",
6 | "templateInfo": {
7 | "name": "mobx",
8 | "typescript": true,
9 | "css": "less"
10 | },
11 | "scripts": {
12 | "build:weapp": "taro build --type weapp",
13 | "build:swan": "taro build --type swan",
14 | "build:alipay": "taro build --type alipay",
15 | "build:tt": "taro build --type tt",
16 | "build:h5": "taro build --type h5",
17 | "build:rn": "taro build --type rn",
18 | "build:qq": "taro build --type qq",
19 | "build:quickapp": "taro build --type quickapp",
20 | "dev:weapp": "npm run build:weapp -- --watch",
21 | "dev:swan": "npm run build:swan -- --watch",
22 | "dev:alipay": "npm run build:alipay -- --watch",
23 | "dev:tt": "npm run build:tt -- --watch",
24 | "dev:h5": "npm run build:h5 -- --watch",
25 | "dev:rn": "npm run build:rn -- --watch",
26 | "dev:qq": "npm run build:qq -- --watch",
27 | "dev:quickapp": "npm run build:quickapp -- --watch"
28 | },
29 | "browserslist": [
30 | "last 3 versions",
31 | "Android >= 4.1",
32 | "ios >= 8"
33 | ],
34 | "author": "",
35 | "license": "MIT",
36 | "dependencies": {
37 | "@babel/runtime": "^7.7.7",
38 | "@tarojs/cli": "3.4.1",
39 | "@tarojs/components": "3.4.1",
40 | "@tarojs/plugin-framework-react": "3.4.1",
41 | "@tarojs/react": "3.4.1",
42 | "@tarojs/runtime": "3.4.1",
43 | "@tarojs/taro": "3.4.1",
44 | "mobx": "^4.8.0",
45 | "mobx-react": "^6.1.4",
46 | "react": "^17.0.0",
47 | "react-dom": "^17.0.0"
48 | },
49 | "devDependencies": {
50 | "@babel/core": "^7.8.0",
51 | "@tarojs/mini-runner": "3.4.1",
52 | "@tarojs/webpack-runner": "3.4.1",
53 | "@types/react": "^17.0.2",
54 | "@types/webpack-env": "^1.13.6",
55 | "@typescript-eslint/eslint-plugin": "^4.15.1",
56 | "@typescript-eslint/parser": "^4.15.1",
57 | "babel-preset-taro": "3.4.1",
58 | "eslint": "^6.8.0",
59 | "eslint-config-taro": "3.4.1",
60 | "eslint-plugin-import": "^2.12.0",
61 | "eslint-plugin-react": "^7.8.2",
62 | "eslint-plugin-react-hooks": "^4.2.0",
63 | "react-interpreter": "^0.3.0",
64 | "stylelint": "9.3.0",
65 | "typescript": "^4.1.0"
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/demos/taro-demo/project.config.json:
--------------------------------------------------------------------------------
1 | {
2 | "miniprogramRoot": "dist/",
3 | "projectname": "taro-demo",
4 | "description": "react-interpreter 的 Taro Demo",
5 | "appid": "touristappid",
6 | "setting": {
7 | "urlCheck": false,
8 | "es6": false,
9 | "enhance": false,
10 | "postcss": false,
11 | "preloadBackgroundData": false,
12 | "minified": false,
13 | "newFeature": false,
14 | "coverView": true,
15 | "nodeModules": false,
16 | "autoAudits": false,
17 | "showShadowRootInWxmlPanel": true,
18 | "scopeDataCheck": false,
19 | "uglifyFileName": false,
20 | "checkInvalidKey": true,
21 | "checkSiteMap": true,
22 | "uploadWithSourceMap": true,
23 | "compileHotReLoad": false,
24 | "lazyloadPlaceholderEnable": false,
25 | "useMultiFrameRuntime": true,
26 | "useApiHook": true,
27 | "useApiHostProcess": true,
28 | "babelSetting": {
29 | "ignore": [],
30 | "disablePlugins": [],
31 | "outputPath": ""
32 | },
33 | "enableEngineNative": false,
34 | "useIsolateContext": false,
35 | "userConfirmedBundleSwitch": false,
36 | "packNpmManually": false,
37 | "packNpmRelationList": [],
38 | "minifyWXSS": true,
39 | "disableUseStrict": false,
40 | "minifyWXML": true,
41 | "showES6CompileOption": false,
42 | "useCompilerPlugins": false
43 | },
44 | "compileType": "miniprogram",
45 | "libVersion": "2.22.0",
46 | "condition": {}
47 | }
--------------------------------------------------------------------------------
/demos/taro-demo/project.tt.json:
--------------------------------------------------------------------------------
1 | {
2 | "miniprogramRoot": "./",
3 | "projectname": "taro-demo",
4 | "description": "react-interpreter 的 Taro Demo",
5 | "appid": "touristappid",
6 | "setting": {
7 | "urlCheck": true,
8 | "es6": false,
9 | "postcss": false,
10 | "minified": false
11 | },
12 | "compileType": "miniprogram"
13 | }
14 |
--------------------------------------------------------------------------------
/demos/taro-demo/src/app.config.ts:
--------------------------------------------------------------------------------
1 | export default defineAppConfig({
2 | pages: [
3 | 'pages/index/index'
4 | ],
5 | window: {
6 | backgroundTextStyle: 'light',
7 | navigationBarBackgroundColor: '#fff',
8 | navigationBarTitleText: 'WeChat',
9 | navigationBarTextStyle: 'black'
10 | }
11 | })
12 |
--------------------------------------------------------------------------------
/demos/taro-demo/src/app.less:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuchangming/react-interpreter/e8c5b0bfc2b5dfa381afc242d9dfd4a3f351e588/demos/taro-demo/src/app.less
--------------------------------------------------------------------------------
/demos/taro-demo/src/app.tsx:
--------------------------------------------------------------------------------
1 | import { Component } from 'react'
2 | import './app.less'
3 |
4 | class App extends Component {
5 | componentDidMount() {}
6 |
7 | componentDidShow() {}
8 |
9 | componentDidHide() {}
10 |
11 | componentDidCatchError() {}
12 |
13 | // this.props.children 就是要渲染的页面
14 | render() {
15 | return this.props.children
16 | }
17 | }
18 |
19 | export default App
20 |
--------------------------------------------------------------------------------
/demos/taro-demo/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/demos/taro-demo/src/pages/index/index.config.ts:
--------------------------------------------------------------------------------
1 | export default definePageConfig({
2 | navigationBarTitleText: 'taro-demo'
3 | })
4 |
--------------------------------------------------------------------------------
/demos/taro-demo/src/pages/index/index.less:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuchangming/react-interpreter/e8c5b0bfc2b5dfa381afc242d9dfd4a3f351e588/demos/taro-demo/src/pages/index/index.less
--------------------------------------------------------------------------------
/demos/taro-demo/src/pages/index/index.tsx:
--------------------------------------------------------------------------------
1 | import { ReactInterpreter } from 'react-interpreter'
2 | import Taro from '@tarojs/taro'
3 | import * as taroComps from '@tarojs/components'
4 | import { Component } from 'react'
5 | import './index.less'
6 |
7 | const codeString = `
8 | function MyReactInterpreterComp() {
9 | return /*#__PURE__*/ React.createElement(
10 | View,
11 | {
12 | style: {
13 | backgroundColor: "pink",
14 | height: "100vh",
15 | display: "flex",
16 | alignItems: "center"
17 | }
18 | },
19 | /*#__PURE__*/ React.createElement(
20 | Button,
21 | {
22 | style: {
23 | backgroundColor: "blue",
24 | color: "#FFFFFF"
25 | },
26 | onClick: function onClick() {
27 | Taro.showToast({
28 | icon: "none",
29 | title: "😂😂😂"
30 | });
31 | }
32 | },
33 | "Click Me!"
34 | )
35 | );
36 | }
37 | `
38 |
39 | class Index extends Component {
40 | render() {
41 | return (
42 |
49 | )
50 | }
51 | }
52 |
53 | export default Index
54 |
--------------------------------------------------------------------------------
/demos/taro-demo/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es2017",
4 | "module": "commonjs",
5 | "removeComments": false,
6 | "preserveConstEnums": true,
7 | "moduleResolution": "node",
8 | "experimentalDecorators": true,
9 | "noImplicitAny": false,
10 | "allowSyntheticDefaultImports": true,
11 | "outDir": "lib",
12 | "noUnusedLocals": true,
13 | "noUnusedParameters": true,
14 | "strictNullChecks": true,
15 | "sourceMap": true,
16 | "baseUrl": ".",
17 | "rootDir": ".",
18 | "jsx": "react-jsx",
19 | "allowJs": true,
20 | "resolveJsonModule": true,
21 | "typeRoots": [
22 | "node_modules/@types",
23 | "global.d.ts"
24 | ]
25 | },
26 | "exclude": [
27 | "node_modules",
28 | "dist"
29 | ],
30 | "compileOnSave": false
31 | }
32 |
--------------------------------------------------------------------------------
/docs/imgs/code-demo.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuchangming/react-interpreter/e8c5b0bfc2b5dfa381afc242d9dfd4a3f351e588/docs/imgs/code-demo.jpeg
--------------------------------------------------------------------------------
/docs/imgs/demo.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wuchangming/react-interpreter/e8c5b0bfc2b5dfa381afc242d9dfd4a3f351e588/docs/imgs/demo.jpeg
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
2 | module.exports = {
3 | preset: 'ts-jest',
4 | testEnvironment: 'node',
5 | }
6 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-interpreter",
3 | "version": "0.3.0",
4 | "description": "react interpreter",
5 | "main": "lib/react-interpreter.js",
6 | "unpkg": "dist/react-interpreter.js",
7 | "module": "es/react-interpreter.js",
8 | "types": "types/index.d.ts",
9 | "files": [
10 | "dist",
11 | "lib",
12 | "es",
13 | "src",
14 | "types"
15 | ],
16 | "scripts": {
17 | "test": "jest",
18 | "build": "rollup -c",
19 | "prepare-release": "npm run test && npm run build",
20 | "release": "npm run prepare-release && npm publish"
21 | },
22 | "repository": {
23 | "type": "git",
24 | "url": "git+https://github.com/wuchangming/react-interpreter.git"
25 | },
26 | "author": "wuchangming",
27 | "license": "MIT",
28 | "bugs": {
29 | "url": "https://github.com/wuchangming/react-interpreter/issues"
30 | },
31 | "homepage": "https://github.com/wuchangming/react-interpreter#readme",
32 | "devDependencies": {
33 | "@babel/core": "^7.17.4",
34 | "@babel/plugin-transform-runtime": "^7.17.0",
35 | "@rollup/plugin-babel": "^5.3.0",
36 | "@rollup/plugin-commonjs": "^21.0.1",
37 | "@rollup/plugin-node-resolve": "^13.1.3",
38 | "@rollup/plugin-replace": "^3.1.0",
39 | "@types/jest": "^27.4.0",
40 | "@types/react": "^17.0.39",
41 | "jest": "^27.5.1",
42 | "react": "^17.0.2",
43 | "rollup": "^2.67.2",
44 | "rollup-plugin-terser": "^7.0.2",
45 | "rollup-plugin-typescript2": "^0.31.2",
46 | "ts-jest": "^27.1.3",
47 | "typescript": "^4.5.5"
48 | },
49 | "dependencies": {
50 | "@babel/runtime": "^7.17.2"
51 | },
52 | "peerDependencies": {
53 | "react": "*"
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/rollup.config.js:
--------------------------------------------------------------------------------
1 | import { env } from 'process'
2 | import nodeResolve from '@rollup/plugin-node-resolve'
3 | import babel from '@rollup/plugin-babel'
4 | import replace from '@rollup/plugin-replace'
5 | import typescript from 'rollup-plugin-typescript2'
6 | import { terser } from 'rollup-plugin-terser'
7 | import commonjs from '@rollup/plugin-commonjs'
8 |
9 | import pkg from './package.json'
10 |
11 | const extensions = ['.ts', '.tsx', '.js']
12 | const noDeclarationFiles = { compilerOptions: { declaration: false } }
13 |
14 | const babelRuntimeVersion = pkg.dependencies['@babel/runtime'].replace(/^[^0-9]*/, '')
15 |
16 | const makeExternalPredicate = (externalArr) => {
17 | if (externalArr.length === 0) {
18 | return () => false
19 | }
20 | const pattern = new RegExp(`^(${externalArr.join('|')})($|/)`)
21 | return (id) => pattern.test(id)
22 | }
23 |
24 | function getBanner(filename) {
25 | const date = new Date(env.SOURCE_DATE_EPOCH ? 1000 * +env.SOURCE_DATE_EPOCH : Date.now()).toUTCString()
26 | return `/*
27 | @license
28 | ${filename} v${pkg.version}
29 | ${date}
30 | @author ${pkg.author}
31 | https://github.com/wuchangming/react-interpreter
32 | Released under the MIT License.
33 | */`
34 | }
35 |
36 | export default async function () {
37 | return [
38 | // CommonJS
39 | {
40 | input: 'src/index.ts',
41 | output: {
42 | banner: getBanner('react-interpreter.js'),
43 | file: 'lib/react-interpreter.js',
44 | format: 'cjs',
45 | indent: false,
46 | },
47 | external: makeExternalPredicate([
48 | ...Object.keys(pkg.dependencies || {}),
49 | ...Object.keys(pkg.peerDependencies || {}),
50 | ]),
51 | plugins: [
52 | commonjs(),
53 | nodeResolve({
54 | extensions,
55 | }),
56 | typescript({ useTsconfigDeclarationDir: true }),
57 | babel({
58 | extensions,
59 | plugins: [['@babel/plugin-transform-runtime', { version: babelRuntimeVersion }]],
60 | babelHelpers: 'runtime',
61 | }),
62 | ],
63 | },
64 |
65 | // ES
66 | {
67 | input: 'src/index.ts',
68 | output: {
69 | banner: getBanner('react-interpreter.js'),
70 | file: 'es/react-interpreter.js',
71 | format: 'es',
72 | indent: false,
73 | },
74 | external: makeExternalPredicate([
75 | ...Object.keys(pkg.dependencies || {}),
76 | ...Object.keys(pkg.peerDependencies || {}),
77 | ]),
78 | plugins: [
79 | commonjs(),
80 | nodeResolve({
81 | extensions,
82 | }),
83 | typescript({ tsconfigOverride: noDeclarationFiles }),
84 | babel({
85 | extensions,
86 | plugins: [
87 | ['@babel/plugin-transform-runtime', { version: babelRuntimeVersion, useESModules: true }],
88 | ],
89 | babelHelpers: 'runtime',
90 | }),
91 | ],
92 | },
93 |
94 | // // ES for Browsers
95 | {
96 | input: 'src/index.ts',
97 | output: {
98 | banner: getBanner('react-interpreter.mjs'),
99 | file: 'es/react-interpreter.mjs',
100 | format: 'es',
101 | indent: false,
102 | },
103 | external: makeExternalPredicate([
104 | ...Object.keys(pkg.dependencies || {}),
105 | ...Object.keys(pkg.peerDependencies || {}),
106 | ]),
107 | plugins: [
108 | commonjs(),
109 | nodeResolve({
110 | extensions,
111 | }),
112 | replace({
113 | preventAssignment: true,
114 | 'process.env.NODE_ENV': JSON.stringify('production'),
115 | }),
116 | typescript({ tsconfigOverride: noDeclarationFiles }),
117 | babel({
118 | extensions,
119 | exclude: 'node_modules/**',
120 | skipPreflightCheck: true,
121 | babelHelpers: 'bundled',
122 | }),
123 | terser({
124 | compress: {
125 | pure_getters: true,
126 | unsafe: true,
127 | unsafe_comps: true,
128 | warnings: false,
129 | },
130 | }),
131 | ],
132 | },
133 |
134 | // // UMD Development
135 | {
136 | input: 'src/index.ts',
137 | output: {
138 | banner: getBanner('react-interpreter.js'),
139 | file: 'dist/react-interpreter.js',
140 | format: 'umd',
141 | name: 'ReactInterpreter',
142 | indent: false,
143 | globals: {
144 | react: 'React',
145 | },
146 | },
147 | external: makeExternalPredicate([
148 | ...Object.keys(pkg.dependencies || {}),
149 | ...Object.keys(pkg.peerDependencies || {}),
150 | ]),
151 | plugins: [
152 | commonjs(),
153 | nodeResolve({
154 | extensions,
155 | }),
156 | typescript({ tsconfigOverride: noDeclarationFiles }),
157 | babel({
158 | extensions,
159 | exclude: 'node_modules/**',
160 | babelHelpers: 'bundled',
161 | }),
162 | replace({
163 | preventAssignment: true,
164 | 'process.env.NODE_ENV': JSON.stringify('development'),
165 | }),
166 | ],
167 | },
168 |
169 | // // UMD Production
170 | {
171 | input: 'src/index.ts',
172 | output: {
173 | banner: getBanner('react-interpreter.min.js'),
174 | file: 'dist/react-interpreter.min.js',
175 | format: 'umd',
176 | name: 'ReactInterpreter',
177 | indent: false,
178 | globals: {
179 | react: 'React',
180 | },
181 | },
182 | external: makeExternalPredicate([
183 | ...Object.keys(pkg.dependencies || {}),
184 | ...Object.keys(pkg.peerDependencies || {}),
185 | ]),
186 | plugins: [
187 | commonjs(),
188 | nodeResolve({
189 | extensions,
190 | }),
191 | typescript({ tsconfigOverride: noDeclarationFiles }),
192 | babel({
193 | extensions,
194 | exclude: 'node_modules/**',
195 | skipPreflightCheck: true,
196 | babelHelpers: 'bundled',
197 | }),
198 | replace({
199 | preventAssignment: true,
200 | 'process.env.NODE_ENV': JSON.stringify('production'),
201 | }),
202 | terser({
203 | compress: {
204 | pure_getters: true,
205 | unsafe: true,
206 | unsafe_comps: true,
207 | warnings: false,
208 | },
209 | }),
210 | ],
211 | },
212 | ]
213 | }
214 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import JSInterpreter from './js-interpreter/interpreter'
2 | import { ReactInterpreter } from './react-interpreter/ReactInterpreter'
3 |
4 | export { JSInterpreter, ReactInterpreter }
5 |
--------------------------------------------------------------------------------
/src/js-interpreter/acorn.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 该文件从 https://github.com/NeilFraser/JS-Interpreter 拷贝过来
3 | * 为什么不直接用 npm 最新版本的 acorn ?因为那个体积比较大,而且这个版本好像也没什么问题
4 | *
5 | */
6 | /* eslint-disable no-shadow */
7 | /* eslint-disable no-undef */
8 | // Acorn is a tiny, fast JavaScript parser written in JavaScript.
9 | //
10 | // Acorn was written by Marijn Haverbeke and released under an MIT
11 | // license. The Unicode regexps (for identifiers and whitespace) were
12 | // taken from [Esprima](http://esprima.org) by Ariya Hidayat.
13 | //
14 | // Git repositories for Acorn are available at
15 | //
16 | // http://marijnhaverbeke.nl/git/acorn
17 | // https://github.com/marijnh/acorn.git
18 | //
19 | // Please use the [github bug tracker][ghbt] to report issues.
20 | //
21 | // [ghbt]: https://github.com/marijnh/acorn/issues
22 | //
23 | // This file defines the main parser interface. The library also comes
24 | // with a [error-tolerant parser][dammit] and an
25 | // [abstract syntax tree walker][walk], defined in other files.
26 | //
27 | // [dammit]: acorn_loose.js
28 | // [walk]: util/walk.js
29 |
30 | (function(root, mod) {
31 | if (typeof exports == "object" && typeof module == "object") return mod(exports); // CommonJS
32 | if (typeof define == "function" && define.amd) return define(["exports"], mod); // AMD
33 | mod(root.acorn || (root.acorn = {})); // Plain browser env
34 | })(this, function(exports) {
35 | "use strict";
36 |
37 | exports.version = "0.4.1";
38 |
39 | // The main exported interface (under `self.acorn` when in the
40 | // browser) is a `parse` function that takes a code string and
41 | // returns an abstract syntax tree as specified by [Mozilla parser
42 | // API][api], with the caveat that the SpiderMonkey-specific syntax
43 | // (`let`, `yield`, inline XML, etc) is not recognized.
44 | //
45 | // [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API
46 |
47 | var options, input, inputLen, sourceFile;
48 |
49 | exports.parse = function(inpt, opts) {
50 | input = String(inpt); inputLen = input.length;
51 | setOptions(opts);
52 | initTokenState();
53 | return parseTopLevel(options.program);
54 | };
55 |
56 | // A second optional argument can be given to further configure
57 | // the parser process. These options are recognized:
58 |
59 | var defaultOptions = exports.defaultOptions = {
60 | // `ecmaVersion` indicates the ECMAScript version to parse. Must
61 | // be either 3 or 5. This
62 | // influences support for strict mode, the set of reserved words, and
63 | // support for getters and setter.
64 | ecmaVersion: 5,
65 | // Turn on `strictSemicolons` to prevent the parser from doing
66 | // automatic semicolon insertion.
67 | strictSemicolons: false,
68 | // When `allowTrailingCommas` is false, the parser will not allow
69 | // trailing commas in array and object literals.
70 | allowTrailingCommas: true,
71 | // By default, reserved words are not enforced. Enable
72 | // `forbidReserved` to enforce them.
73 | forbidReserved: false,
74 | // When `locations` is on, `loc` properties holding objects with
75 | // `start` and `end` properties in `{line, column}` form (with
76 | // line being 1-based and column 0-based) will be attached to the
77 | // nodes.
78 | locations: false,
79 | // A function can be passed as `onComment` option, which will
80 | // cause Acorn to call that function with `(block, text, start,
81 | // end)` parameters whenever a comment is skipped. `block` is a
82 | // boolean indicating whether this is a block (`/* */`) comment,
83 | // `text` is the content of the comment, and `start` and `end` are
84 | // character offsets that denote the start and end of the comment.
85 | // When the `locations` option is on, two more parameters are
86 | // passed, the full `{line, column}` locations of the start and
87 | // end of the comments.
88 | onComment: null,
89 | // Nodes have their start and end characters offsets recorded in
90 | // `start` and `end` properties (directly on the node, rather than
91 | // the `loc` object, which holds line/column data. To also add a
92 | // [semi-standardized][range] `range` property holding a `[start,
93 | // end]` array with the same numbers, set the `ranges` option to
94 | // `true`.
95 | //
96 | // [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678
97 | ranges: false,
98 | // It is possible to parse multiple files into a single AST by
99 | // passing the tree produced by parsing the first file as
100 | // `program` option in subsequent parses. This will add the
101 | // toplevel forms of the parsed file to the `Program` (top) node
102 | // of an existing parse tree.
103 | program: null,
104 | // When `location` is on, you can pass this to record the source
105 | // file in every node's `loc` object.
106 | sourceFile: null,
107 | // This value, if given, is stored in every node, whether
108 | // `location` is on or off.
109 | directSourceFile: null
110 | };
111 |
112 | function setOptions(opts) {
113 | options = opts || {};
114 | for (var opt in defaultOptions) if (!Object.prototype.hasOwnProperty.call(options, opt))
115 | options[opt] = defaultOptions[opt];
116 | sourceFile = options.sourceFile || null;
117 | }
118 |
119 | // The `getLineInfo` function is mostly useful when the
120 | // `locations` option is off (for performance reasons) and you
121 | // want to find the line/column position for a given character
122 | // offset. `input` should be the code string that the offset refers
123 | // into.
124 |
125 | var getLineInfo = exports.getLineInfo = function(input, offset) {
126 | for (var line = 1, cur = 0;;) {
127 | lineBreak.lastIndex = cur;
128 | var match = lineBreak.exec(input);
129 | if (match && match.index < offset) {
130 | ++line;
131 | cur = match.index + match[0].length;
132 | } else break;
133 | }
134 | return {line: line, column: offset - cur};
135 | };
136 |
137 | // Acorn is organized as a tokenizer and a recursive-descent parser.
138 | // The `tokenize` export provides an interface to the tokenizer.
139 | // Because the tokenizer is optimized for being efficiently used by
140 | // the Acorn parser itself, this interface is somewhat crude and not
141 | // very modular. Performing another parse or call to `tokenize` will
142 | // reset the internal state, and invalidate existing tokenizers.
143 |
144 | exports.tokenize = function(inpt, opts) {
145 | input = String(inpt); inputLen = input.length;
146 | setOptions(opts);
147 | initTokenState();
148 |
149 | var t = {};
150 | function getToken(forceRegexp) {
151 | readToken(forceRegexp);
152 | t.start = tokStart; t.end = tokEnd;
153 | t.startLoc = tokStartLoc; t.endLoc = tokEndLoc;
154 | t.type = tokType; t.value = tokVal;
155 | return t;
156 | }
157 | getToken.jumpTo = function(pos, reAllowed) {
158 | tokPos = pos;
159 | if (options.locations) {
160 | tokCurLine = 1;
161 | tokLineStart = lineBreak.lastIndex = 0;
162 | var match;
163 | while ((match = lineBreak.exec(input)) && match.index < pos) {
164 | ++tokCurLine;
165 | tokLineStart = match.index + match[0].length;
166 | }
167 | }
168 | tokRegexpAllowed = reAllowed;
169 | skipSpace();
170 | };
171 | return getToken;
172 | };
173 |
174 | // State is kept in (closure-)global variables. We already saw the
175 | // `options`, `input`, and `inputLen` variables above.
176 |
177 | // The current position of the tokenizer in the input.
178 |
179 | var tokPos;
180 |
181 | // The start and end offsets of the current token.
182 |
183 | var tokStart, tokEnd;
184 |
185 | // When `options.locations` is true, these hold objects
186 | // containing the tokens start and end line/column pairs.
187 |
188 | var tokStartLoc, tokEndLoc;
189 |
190 | // The type and value of the current token. Token types are objects,
191 | // named by variables against which they can be compared, and
192 | // holding properties that describe them (indicating, for example,
193 | // the precedence of an infix operator, and the original name of a
194 | // keyword token). The kind of value that's held in `tokVal` depends
195 | // on the type of the token. For literals, it is the literal value,
196 | // for operators, the operator name, and so on.
197 |
198 | var tokType, tokVal;
199 |
200 | // Interal state for the tokenizer. To distinguish between division
201 | // operators and regular expressions, it remembers whether the last
202 | // token was one that is allowed to be followed by an expression.
203 | // (If it is, a slash is probably a regexp, if it isn't it's a
204 | // division operator. See the `parseStatement` function for a
205 | // caveat.)
206 |
207 | var tokRegexpAllowed;
208 |
209 | // When `options.locations` is true, these are used to keep
210 | // track of the current line, and know when a new line has been
211 | // entered.
212 |
213 | var tokCurLine, tokLineStart;
214 |
215 | // These store the position of the previous token, which is useful
216 | // when finishing a node and assigning its `end` position.
217 |
218 | var lastStart, lastEnd, lastEndLoc;
219 |
220 | // This is the parser's state. `inFunction` is used to reject
221 | // `return` statements outside of functions, `labels` to verify that
222 | // `break` and `continue` have somewhere to jump to, and `strict`
223 | // indicates whether strict mode is on.
224 |
225 | var inFunction, labels, strict;
226 |
227 | // This function is used to raise exceptions on parse errors. It
228 | // takes an offset integer (into the current `input`) to indicate
229 | // the location of the error, attaches the position to the end
230 | // of the error message, and then raises a `SyntaxError` with that
231 | // message.
232 |
233 | function raise(pos, message) {
234 | var loc = getLineInfo(input, pos);
235 | message += " (" + loc.line + ":" + loc.column + ")";
236 | var err = new SyntaxError(message);
237 | err.pos = pos; err.loc = loc; err.raisedAt = tokPos;
238 | throw err;
239 | }
240 |
241 | // Reused empty array added for node fields that are always empty.
242 |
243 | var empty = [];
244 |
245 | // ## Token types
246 |
247 | // The assignment of fine-grained, information-carrying type objects
248 | // allows the tokenizer to store the information it has about a
249 | // token in a way that is very cheap for the parser to look up.
250 |
251 | // All token type variables start with an underscore, to make them
252 | // easy to recognize.
253 |
254 | // These are the general types. The `type` property is only used to
255 | // make them recognizeable when debugging.
256 |
257 | var _num = {type: "num"}, _regexp = {type: "regexp"}, _string = {type: "string"};
258 | var _name = {type: "name"}, _eof = {type: "eof"};
259 |
260 | // Keyword tokens. The `keyword` property (also used in keyword-like
261 | // operators) indicates that the token originated from an
262 | // identifier-like word, which is used when parsing property names.
263 | //
264 | // The `beforeExpr` property is used to disambiguate between regular
265 | // expressions and divisions. It is set on all token types that can
266 | // be followed by an expression (thus, a slash after them would be a
267 | // regular expression).
268 | //
269 | // `isLoop` marks a keyword as starting a loop, which is important
270 | // to know when parsing a label, in order to allow or disallow
271 | // continue jumps to that label.
272 |
273 | var _break = {keyword: "break"}, _case = {keyword: "case", beforeExpr: true}, _catch = {keyword: "catch"};
274 | var _continue = {keyword: "continue"}, _debugger = {keyword: "debugger"}, _default = {keyword: "default"};
275 | var _do = {keyword: "do", isLoop: true}, _else = {keyword: "else", beforeExpr: true};
276 | var _finally = {keyword: "finally"}, _for = {keyword: "for", isLoop: true}, _function = {keyword: "function"};
277 | var _if = {keyword: "if"}, _return = {keyword: "return", beforeExpr: true}, _switch = {keyword: "switch"};
278 | var _throw = {keyword: "throw", beforeExpr: true}, _try = {keyword: "try"}, _var = {keyword: "var"};
279 | var _while = {keyword: "while", isLoop: true}, _with = {keyword: "with"}, _new = {keyword: "new", beforeExpr: true};
280 | var _this = {keyword: "this"};
281 |
282 | // The keywords that denote values.
283 |
284 | var _null = {keyword: "null", atomValue: null}, _true = {keyword: "true", atomValue: true};
285 | var _false = {keyword: "false", atomValue: false};
286 |
287 | // Some keywords are treated as regular operators. `in` sometimes
288 | // (when parsing `for`) needs to be tested against specifically, so
289 | // we assign a variable name to it for quick comparing.
290 |
291 | var _in = {keyword: "in", binop: 7, beforeExpr: true};
292 |
293 | // Map keyword names to token types.
294 |
295 | var keywordTypes = {"break": _break, "case": _case, "catch": _catch,
296 | "continue": _continue, "debugger": _debugger, "default": _default,
297 | "do": _do, "else": _else, "finally": _finally, "for": _for,
298 | "function": _function, "if": _if, "return": _return, "switch": _switch,
299 | "throw": _throw, "try": _try, "var": _var, "while": _while, "with": _with,
300 | "null": _null, "true": _true, "false": _false, "new": _new, "in": _in,
301 | "instanceof": {keyword: "instanceof", binop: 7, beforeExpr: true}, "this": _this,
302 | "typeof": {keyword: "typeof", prefix: true, beforeExpr: true},
303 | "void": {keyword: "void", prefix: true, beforeExpr: true},
304 | "delete": {keyword: "delete", prefix: true, beforeExpr: true}};
305 |
306 | // Punctuation token types. Again, the `type` property is purely for debugging.
307 |
308 | var _bracketL = {type: "[", beforeExpr: true}, _bracketR = {type: "]"}, _braceL = {type: "{", beforeExpr: true};
309 | var _braceR = {type: "}"}, _parenL = {type: "(", beforeExpr: true}, _parenR = {type: ")"};
310 | var _comma = {type: ",", beforeExpr: true}, _semi = {type: ";", beforeExpr: true};
311 | var _colon = {type: ":", beforeExpr: true}, _dot = {type: "."}, _question = {type: "?", beforeExpr: true};
312 |
313 | // Operators. These carry several kinds of properties to help the
314 | // parser use them properly (the presence of these properties is
315 | // what categorizes them as operators).
316 | //
317 | // `binop`, when present, specifies that this operator is a binary
318 | // operator, and will refer to its precedence.
319 | //
320 | // `prefix` and `postfix` mark the operator as a prefix or postfix
321 | // unary operator. `isUpdate` specifies that the node produced by
322 | // the operator should be of type UpdateExpression rather than
323 | // simply UnaryExpression (`++` and `--`).
324 | //
325 | // `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as
326 | // binary operators with a very low precedence, that should result
327 | // in AssignmentExpression nodes.
328 |
329 | var _slash = {binop: 10, beforeExpr: true}, _eq = {isAssign: true, beforeExpr: true};
330 | var _assign = {isAssign: true, beforeExpr: true};
331 | var _incDec = {postfix: true, prefix: true, isUpdate: true}, _prefix = {prefix: true, beforeExpr: true};
332 | var _logicalOR = {binop: 1, beforeExpr: true};
333 | var _logicalAND = {binop: 2, beforeExpr: true};
334 | var _bitwiseOR = {binop: 3, beforeExpr: true};
335 | var _bitwiseXOR = {binop: 4, beforeExpr: true};
336 | var _bitwiseAND = {binop: 5, beforeExpr: true};
337 | var _equality = {binop: 6, beforeExpr: true};
338 | var _relational = {binop: 7, beforeExpr: true};
339 | var _bitShift = {binop: 8, beforeExpr: true};
340 | var _plusMin = {binop: 9, prefix: true, beforeExpr: true};
341 | var _multiplyModulo = {binop: 10, beforeExpr: true};
342 |
343 | // Provide access to the token types for external users of the
344 | // tokenizer.
345 |
346 | exports.tokTypes = {bracketL: _bracketL, bracketR: _bracketR, braceL: _braceL, braceR: _braceR,
347 | parenL: _parenL, parenR: _parenR, comma: _comma, semi: _semi, colon: _colon,
348 | dot: _dot, question: _question, slash: _slash, eq: _eq, name: _name, eof: _eof,
349 | num: _num, regexp: _regexp, string: _string};
350 | for (var kw in keywordTypes) exports.tokTypes["_" + kw] = keywordTypes[kw];
351 |
352 | // Acorn's original code built up functions using strings for maximum efficiency.
353 | // However, this triggered a CSP unsafe-eval requirement. Here's a slower, but
354 | // simpler approach. -- Neil Fraser, January 2022.
355 | // https://github.com/NeilFraser/JS-Interpreter/issues/228
356 | function makePredicate(words) {
357 | words = words.split(" ");
358 | var set = Object.create(null);
359 | for (var i = 0; i < words.length; i++) {
360 | set[words[i]] = true;
361 | }
362 | return function(str) {
363 | return set[str] || false;
364 | };
365 | }
366 |
367 | // The ECMAScript 3 reserved word list.
368 |
369 | var isReservedWord3 = makePredicate("abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile");
370 |
371 | // ECMAScript 5 reserved words.
372 |
373 | var isReservedWord5 = makePredicate("class enum extends super const export import");
374 |
375 | // The additional reserved words in strict mode.
376 |
377 | var isStrictReservedWord = makePredicate("implements interface let package private protected public static yield");
378 |
379 | // The forbidden variable names in strict mode.
380 |
381 | var isStrictBadIdWord = makePredicate("eval arguments");
382 |
383 | // And the keywords.
384 |
385 | var isKeyword = makePredicate("break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this");
386 |
387 | // ## Character categories
388 |
389 | // Big ugly regular expressions that match characters in the
390 | // whitespace, identifier, and identifier-start categories. These
391 | // are only applied when a character is found to actually have a
392 | // code point above 128.
393 |
394 | var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/;
395 | var nonASCIIidentifierStartChars = "\xaa\xb5\xba\xc0-\xd6\xd8-\xf6\xf8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc";
396 | var nonASCIIidentifierChars = "\u0300-\u036f\u0483-\u0487\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u0610-\u061a\u0620-\u0649\u0672-\u06d3\u06e7-\u06e8\u06fb-\u06fc\u0730-\u074a\u0800-\u0814\u081b-\u0823\u0825-\u0827\u0829-\u082d\u0840-\u0857\u08e4-\u08fe\u0900-\u0903\u093a-\u093c\u093e-\u094f\u0951-\u0957\u0962-\u0963\u0966-\u096f\u0981-\u0983\u09bc\u09be-\u09c4\u09c7\u09c8\u09d7\u09df-\u09e0\u0a01-\u0a03\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a66-\u0a71\u0a75\u0a81-\u0a83\u0abc\u0abe-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ae2-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b3c\u0b3e-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5f-\u0b60\u0b66-\u0b6f\u0b82\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c62-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0cbc\u0cbe-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0ce2-\u0ce3\u0ce6-\u0cef\u0d02\u0d03\u0d46-\u0d48\u0d57\u0d62-\u0d63\u0d66-\u0d6f\u0d82\u0d83\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e34-\u0e3a\u0e40-\u0e45\u0e50-\u0e59\u0eb4-\u0eb9\u0ec8-\u0ecd\u0ed0-\u0ed9\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f41-\u0f47\u0f71-\u0f84\u0f86-\u0f87\u0f8d-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1029\u1040-\u1049\u1067-\u106d\u1071-\u1074\u1082-\u108d\u108f-\u109d\u135d-\u135f\u170e-\u1710\u1720-\u1730\u1740-\u1750\u1772\u1773\u1780-\u17b2\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1920-\u192b\u1930-\u193b\u1951-\u196d\u19b0-\u19c0\u19c8-\u19c9\u19d0-\u19d9\u1a00-\u1a15\u1a20-\u1a53\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1b46-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1bb0-\u1bb9\u1be6-\u1bf3\u1c00-\u1c22\u1c40-\u1c49\u1c5b-\u1c7d\u1cd0-\u1cd2\u1d00-\u1dbe\u1e01-\u1f15\u200c\u200d\u203f\u2040\u2054\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2d81-\u2d96\u2de0-\u2dff\u3021-\u3028\u3099\u309a\ua640-\ua66d\ua674-\ua67d\ua69f\ua6f0-\ua6f1\ua7f8-\ua800\ua806\ua80b\ua823-\ua827\ua880-\ua881\ua8b4-\ua8c4\ua8d0-\ua8d9\ua8f3-\ua8f7\ua900-\ua909\ua926-\ua92d\ua930-\ua945\ua980-\ua983\ua9b3-\ua9c0\uaa00-\uaa27\uaa40-\uaa41\uaa4c-\uaa4d\uaa50-\uaa59\uaa7b\uaae0-\uaae9\uaaf2-\uaaf3\uabc0-\uabe1\uabec\uabed\uabf0-\uabf9\ufb20-\ufb28\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\uff10-\uff19\uff3f";
397 | var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
398 | var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");
399 |
400 | // Whether a single character denotes a newline.
401 |
402 | var newline = /[\n\r\u2028\u2029]/;
403 |
404 | // Matches a whole line break (where CRLF is considered a single
405 | // line break). Used to count lines.
406 |
407 | var lineBreak = /\r\n|[\n\r\u2028\u2029]/g;
408 |
409 | // Test whether a given character code starts an identifier.
410 |
411 | var isIdentifierStart = exports.isIdentifierStart = function(code) {
412 | if (code < 65) return code === 36;
413 | if (code < 91) return true;
414 | if (code < 97) return code === 95;
415 | if (code < 123)return true;
416 | return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code));
417 | };
418 |
419 | // Test whether a given character is part of an identifier.
420 |
421 | var isIdentifierChar = exports.isIdentifierChar = function(code) {
422 | if (code < 48) return code === 36;
423 | if (code < 58) return true;
424 | if (code < 65) return false;
425 | if (code < 91) return true;
426 | if (code < 97) return code === 95;
427 | if (code < 123)return true;
428 | return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));
429 | };
430 |
431 | // ## Tokenizer
432 |
433 | // These are used when `options.locations` is on, for the
434 | // `tokStartLoc` and `tokEndLoc` properties.
435 |
436 | function line_loc_t() {
437 | this.line = tokCurLine;
438 | this.column = tokPos - tokLineStart;
439 | }
440 |
441 | // Reset the token state. Used at the start of a parse.
442 |
443 | function initTokenState() {
444 | tokCurLine = 1;
445 | tokPos = tokLineStart = 0;
446 | tokRegexpAllowed = true;
447 | skipSpace();
448 | }
449 |
450 | // Called at the end of every token. Sets `tokEnd`, `tokVal`, and
451 | // `tokRegexpAllowed`, and skips the space after the token, so that
452 | // the next one's `tokStart` will point at the right position.
453 |
454 | function finishToken(type, val) {
455 | tokEnd = tokPos;
456 | if (options.locations) tokEndLoc = new line_loc_t;
457 | tokType = type;
458 | skipSpace();
459 | tokVal = val;
460 | tokRegexpAllowed = type.beforeExpr;
461 | }
462 |
463 | function skipBlockComment() {
464 | var startLoc = options.onComment && options.locations && new line_loc_t;
465 | var start = tokPos, end = input.indexOf("*/", tokPos += 2);
466 | if (end === -1) raise(tokPos - 2, "Unterminated comment");
467 | tokPos = end + 2;
468 | if (options.locations) {
469 | lineBreak.lastIndex = start;
470 | var match;
471 | while ((match = lineBreak.exec(input)) && match.index < tokPos) {
472 | ++tokCurLine;
473 | tokLineStart = match.index + match[0].length;
474 | }
475 | }
476 | if (options.onComment)
477 | options.onComment(true, input.slice(start + 2, end), start, tokPos,
478 | startLoc, options.locations && new line_loc_t);
479 | }
480 |
481 | function skipLineComment() {
482 | var start = tokPos;
483 | var startLoc = options.onComment && options.locations && new line_loc_t;
484 | var ch = input.charCodeAt(tokPos+=2);
485 | while (tokPos < inputLen && ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8233) {
486 | ++tokPos;
487 | ch = input.charCodeAt(tokPos);
488 | }
489 | if (options.onComment)
490 | options.onComment(false, input.slice(start + 2, tokPos), start, tokPos,
491 | startLoc, options.locations && new line_loc_t);
492 | }
493 |
494 | // Called at the start of the parse and after every token. Skips
495 | // whitespace and comments, and.
496 |
497 | function skipSpace() {
498 | while (tokPos < inputLen) {
499 | var ch = input.charCodeAt(tokPos);
500 | if (ch === 32) { // ' '
501 | ++tokPos;
502 | } else if (ch === 13) {
503 | ++tokPos;
504 | var next = input.charCodeAt(tokPos);
505 | if (next === 10) {
506 | ++tokPos;
507 | }
508 | if (options.locations) {
509 | ++tokCurLine;
510 | tokLineStart = tokPos;
511 | }
512 | } else if (ch === 10 || ch === 8232 || ch === 8233) {
513 | ++tokPos;
514 | if (options.locations) {
515 | ++tokCurLine;
516 | tokLineStart = tokPos;
517 | }
518 | } else if (ch > 8 && ch < 14) {
519 | ++tokPos;
520 | } else if (ch === 47) { // '/'
521 | var next = input.charCodeAt(tokPos + 1);
522 | if (next === 42) { // '*'
523 | skipBlockComment();
524 | } else if (next === 47) { // '/'
525 | skipLineComment();
526 | } else break;
527 | } else if (ch === 160) { // '\xa0'
528 | ++tokPos;
529 | } else if (ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) {
530 | ++tokPos;
531 | } else {
532 | break;
533 | }
534 | }
535 | }
536 |
537 | // ### Token reading
538 |
539 | // This is the function that is called to fetch the next token. It
540 | // is somewhat obscure, because it works in character codes rather
541 | // than characters, and because operator parsing has been inlined
542 | // into it.
543 | //
544 | // All in the name of speed.
545 | //
546 | // The `forceRegexp` parameter is used in the one case where the
547 | // `tokRegexpAllowed` trick does not work. See `parseStatement`.
548 |
549 | function readToken_dot() {
550 | var next = input.charCodeAt(tokPos + 1);
551 | if (next >= 48 && next <= 57) return readNumber(true);
552 | ++tokPos;
553 | return finishToken(_dot);
554 | }
555 |
556 | function readToken_slash() { // '/'
557 | var next = input.charCodeAt(tokPos + 1);
558 | if (tokRegexpAllowed) {++tokPos; return readRegexp();}
559 | if (next === 61) return finishOp(_assign, 2);
560 | return finishOp(_slash, 1);
561 | }
562 |
563 | function readToken_mult_modulo() { // '%*'
564 | var next = input.charCodeAt(tokPos + 1);
565 | if (next === 61) return finishOp(_assign, 2);
566 | return finishOp(_multiplyModulo, 1);
567 | }
568 |
569 | function readToken_pipe_amp(code) { // '|&'
570 | var next = input.charCodeAt(tokPos + 1);
571 | if (next === code) return finishOp(code === 124 ? _logicalOR : _logicalAND, 2);
572 | if (next === 61) return finishOp(_assign, 2);
573 | return finishOp(code === 124 ? _bitwiseOR : _bitwiseAND, 1);
574 | }
575 |
576 | function readToken_caret() { // '^'
577 | var next = input.charCodeAt(tokPos + 1);
578 | if (next === 61) return finishOp(_assign, 2);
579 | return finishOp(_bitwiseXOR, 1);
580 | }
581 |
582 | function readToken_plus_min(code) { // '+-'
583 | var next = input.charCodeAt(tokPos + 1);
584 | if (next === code) {
585 | if (next == 45 && input.charCodeAt(tokPos + 2) == 62 &&
586 | newline.test(input.slice(lastEnd, tokPos))) {
587 | // A `-->` line comment
588 | tokPos += 3;
589 | skipLineComment();
590 | skipSpace();
591 | return readToken();
592 | }
593 | return finishOp(_incDec, 2);
594 | }
595 | if (next === 61) return finishOp(_assign, 2);
596 | return finishOp(_plusMin, 1);
597 | }
598 |
599 | function readToken_lt_gt(code) { // '<>'
600 | var next = input.charCodeAt(tokPos + 1);
601 | var size = 1;
602 | if (next === code) {
603 | size = code === 62 && input.charCodeAt(tokPos + 2) === 62 ? 3 : 2;
604 | if (input.charCodeAt(tokPos + size) === 61) return finishOp(_assign, size + 1);
605 | return finishOp(_bitShift, size);
606 | }
607 | if (next == 33 && code == 60 && input.charCodeAt(tokPos + 2) == 45 &&
608 | input.charCodeAt(tokPos + 3) == 45) {
609 | // `