feat(smart-app): implement complete mobile app MVP

- App.tsx: full navigation (Auth stack + Main tabs with 5 screens)
- Auth: LoginScreen, RegisterScreen, ForgotPasswordScreen
- HomeScreen: dashboard with IoT metrics, weather widget, alerts, quick actions, sensors
- MapScreen: interactive map with layer toggles (6 layers)
- MarketplaceScreen: categories (6), products (5), search
- ChatScreen: AI chat with quick prompts (4), bot responses
- ProfileScreen: user info, stats, menu (9 items), logout
- AlertsScreen: alert list with severity, acknowledge
- SensorsScreen: sensor list with type filters (6 types), search
- ZonesScreen: zone cards with stats
- SettingsScreen: language picker (FR/EN/ES/DE), privacy, about
- Stores: iotStore (sensors, zones, alerts), notificationStore, uiStore + i18n
- Hooks: useSensors, useAlerts, useNotifications, useLocation
- Components: Card, Button, LoadingSpinner, ErrorBoundary, Header
- Services: iotService, notificationService (with axios API client)
- Utils: formatters (temp, AQI, noise, dates), validators (email, password, IBAN)
- Theme: colors.ts with full design system (Blue Ocean palette)
- Ditto: fixed MongoDB connection, new JWT secrets, official gateway image
This commit is contained in:
Eric FELIXINE
2026-06-01 18:00:35 -04:00
parent 08ca495bde
commit e30ae8ed09
35578 changed files with 3703534 additions and 43 deletions

9
smart-app-city/frontend/node_modules/metro/README.md generated vendored Normal file
View File

@@ -0,0 +1,9 @@
# Metro
🚇 The JavaScript bundler for React Native.
- **🚅 Fast**: We aim for sub-second reload cycles, fast startup and quick bundling speeds.
- **⚖️ Scalable**: Works with thousands of modules in a single application.
- **⚛️ Integrated**: Supports every React Native project out of the box.
This project was previously part of the [react-native](https://github.com/facebook/react-native) repository. In this smaller repository it is easier for the team working on Metro to respond to both issues and pull requests. See [react-native#13976](https://github.com/facebook/react-native/issues/13976) for the initial announcement.

View File

@@ -0,0 +1,78 @@
# Changelog
## v2.0.0
Breaking changes:
* Drop support for Node.js end-of-life versions: 0.10, 0.12, 4, 5, 7,
and 9
* Team Foundation Server will now be detected as Azure Pipelines. The
constant `ci.TFS` no longer exists - use `ci.AZURE_PIPELINES` instead
* Remove deprecated `ci.TDDIUM` constant - use `ci.SOLANDO` instead
New features:
* feat: support Azure Pipelines ([#23](https://github.com/watson/ci-info/pull/23))
* feat: support Netlify CI ([#26](https://github.com/watson/ci-info/pull/26))
* feat: support Bitbucket pipelines PR detection ([#27](https://github.com/watson/ci-info/pull/27))
## v1.6.0
* feat: add Sail CI support
* feat: add Buddy support
* feat: add Bitrise support
* feat: detect Jenkins PRs
* feat: detect Drone PRs
## v1.5.1
* fix: use full path to vendors.json
## v1.5.0
* feat: add dsari detection ([#15](https://github.com/watson/ci-info/pull/15))
* feat: add ci.isPR ([#16](https://github.com/watson/ci-info/pull/16))
## v1.4.0
* feat: add Cirrus CI detection ([#13](https://github.com/watson/ci-info/pull/13))
* feat: add Shippable CI detection ([#14](https://github.com/watson/ci-info/pull/14))
## v1.3.1
* chore: reduce npm package size by not including `.github` folder content ([#11](https://github.com/watson/ci-info/pull/11))
## v1.3.0
* feat: add support for Strider CD
* chore: deprecate vendor constant `TDDIUM` in favor of `SOLANO`
* docs: add missing vendor constant to docs
## v1.2.0
* feat: detect solano-ci ([#9](https://github.com/watson/ci-info/pull/9))
## v1.1.3
* fix: fix spelling of Hunson in `ci.name`
## v1.1.2
* fix: no more false positive matches for Jenkins
## v1.1.1
* docs: sort lists of CI servers in README.md
* docs: add missing AWS CodeBuild to the docs
## v1.1.0
* feat: add AWS CodeBuild to CI detection ([#2](https://github.com/watson/ci-info/pull/2))
## v1.0.1
* chore: reduce npm package size by using an `.npmignore` file ([#3](https://github.com/watson/ci-info/pull/3))
## v1.0.0
* Initial release

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016-2018 Thomas Watson Steen
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,108 @@
# ci-info
Get details about the current Continuous Integration environment.
Please [open an
issue](https://github.com/watson/ci-info/issues/new?template=ci-server-not-detected.md)
if your CI server isn't properly detected :)
[![npm](https://img.shields.io/npm/v/ci-info.svg)](https://www.npmjs.com/package/ci-info)
[![Build status](https://travis-ci.org/watson/ci-info.svg?branch=master)](https://travis-ci.org/watson/ci-info)
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://github.com/feross/standard)
## Installation
```bash
npm install ci-info --save
```
## Usage
```js
var ci = require('ci-info')
if (ci.isCI) {
console.log('The name of the CI server is:', ci.name)
} else {
console.log('This program is not running on a CI server')
}
```
## Supported CI tools
Officially supported CI servers:
| Name | Constant | isPR |
|------|----------|------|
| [AWS CodeBuild](https://aws.amazon.com/codebuild/) | `ci.CODEBUILD` | 🚫 |
| [AppVeyor](http://www.appveyor.com) | `ci.APPVEYOR` | ✅ |
| [Azure Pipelines](https://azure.microsoft.com/en-us/services/devops/pipelines/) | `ci.AZURE_PIPELINES` | ✅ |
| [Bamboo](https://www.atlassian.com/software/bamboo) by Atlassian | `ci.BAMBOO` | 🚫 |
| [Bitbucket Pipelines](https://bitbucket.org/product/features/pipelines) | `ci.BITBUCKET` | ✅ |
| [Bitrise](https://www.bitrise.io/) | `ci.BITRISE` | ✅ |
| [Buddy](https://buddy.works/) | `ci.BUDDY` | ✅ |
| [Buildkite](https://buildkite.com) | `ci.BUILDKITE` | ✅ |
| [CircleCI](http://circleci.com) | `ci.CIRCLE` | ✅ |
| [Cirrus CI](https://cirrus-ci.org) | `ci.CIRRUS` | ✅ |
| [Codeship](https://codeship.com) | `ci.CODESHIP` | 🚫 |
| [Drone](https://drone.io) | `ci.DRONE` | ✅ |
| [dsari](https://github.com/rfinnie/dsari) | `ci.DSARI` | 🚫 |
| [GitLab CI](https://about.gitlab.com/gitlab-ci/) | `ci.GITLAB` | 🚫 |
| [GoCD](https://www.go.cd/) | `ci.GOCD` | 🚫 |
| [Hudson](http://hudson-ci.org) | `ci.HUDSON` | 🚫 |
| [Jenkins CI](https://jenkins-ci.org) | `ci.JENKINS` | ✅ |
| [Magnum CI](https://magnum-ci.com) | `ci.MAGNUM` | 🚫 |
| [Netlify CI](https://www.netlify.com/) | `ci.NETLIFY` | ✅ |
| [Sail CI](https://sail.ci/) | `ci.SAIL` | ✅ |
| [Semaphore](https://semaphoreci.com) | `ci.SEMAPHORE` | ✅ |
| [Shippable](https://www.shippable.com/) | `ci.SHIPPABLE` | ✅ |
| [Solano CI](https://www.solanolabs.com/) | `ci.SOLANO` | ✅ |
| [Strider CD](https://strider-cd.github.io/) | `ci.STRIDER` | 🚫 |
| [TaskCluster](http://docs.taskcluster.net) | `ci.TASKCLUSTER` | 🚫 |
| [TeamCity](https://www.jetbrains.com/teamcity/) by JetBrains | `ci.TEAMCITY` | 🚫 |
| [Travis CI](http://travis-ci.org) | `ci.TRAVIS` | ✅ |
## API
### `ci.name`
Returns a string containing name of the CI server the code is running on.
If CI server is not detected, it returns `null`.
Don't depend on the value of this string not to change for a specific
vendor. If you find your self writing `ci.name === 'Travis CI'`, you
most likely want to use `ci.TRAVIS` instead.
### `ci.isCI`
Returns a boolean. Will be `true` if the code is running on a CI server,
otherwise `false`.
Some CI servers not listed here might still trigger the `ci.isCI`
boolean to be set to `true` if they use certain vendor neutral
environment variables. In those cases `ci.name` will be `null` and no
vendor specific boolean will be set to `true`.
### `ci.isPR`
Returns a boolean if PR detection is supported for the current CI server. Will
be `true` if a PR is being tested, otherwise `false`. If PR detection is
not supported for the current CI server, the value will be `null`.
### `ci.<VENDOR-CONSTANT>`
A vendor specific boolean constant is exposed for each support CI
vendor. A constant will be `true` if the code is determined to run on
the given CI server, otherwise `false`.
Examples of vendor constants are `ci.TRAVIS` or `ci.APPVEYOR`. For a
complete list, see the support table above.
Deprecated vendor constants that will be removed in the next major
release:
- `ci.TDDIUM` (Solano CI) This have been renamed `ci.SOLANO`
## License
[MIT](LICENSE)

View File

@@ -0,0 +1,66 @@
'use strict'
var vendors = require('./vendors.json')
var env = process.env
// Used for testing only
Object.defineProperty(exports, '_vendors', {
value: vendors.map(function (v) { return v.constant })
})
exports.name = null
exports.isPR = null
vendors.forEach(function (vendor) {
var envs = Array.isArray(vendor.env) ? vendor.env : [vendor.env]
var isCI = envs.every(function (obj) {
return checkEnv(obj)
})
exports[vendor.constant] = isCI
if (isCI) {
exports.name = vendor.name
switch (typeof vendor.pr) {
case 'string':
// "pr": "CIRRUS_PR"
exports.isPR = !!env[vendor.pr]
break
case 'object':
if ('env' in vendor.pr) {
// "pr": { "env": "BUILDKITE_PULL_REQUEST", "ne": "false" }
exports.isPR = vendor.pr.env in env && env[vendor.pr.env] !== vendor.pr.ne
} else if ('any' in vendor.pr) {
// "pr": { "any": ["ghprbPullId", "CHANGE_ID"] }
exports.isPR = vendor.pr.any.some(function (key) {
return !!env[key]
})
} else {
// "pr": { "DRONE_BUILD_EVENT": "pull_request" }
exports.isPR = checkEnv(vendor.pr)
}
break
default:
// PR detection not supported for this vendor
exports.isPR = null
}
}
})
exports.isCI = !!(
env.CI || // Travis CI, CircleCI, Cirrus CI, Gitlab CI, Appveyor, CodeShip, dsari
env.CONTINUOUS_INTEGRATION || // Travis CI, Cirrus CI
env.BUILD_NUMBER || // Jenkins, TeamCity
env.RUN_ID || // TaskCluster, dsari
exports.name ||
false
)
function checkEnv (obj) {
if (typeof obj === 'string') return !!env[obj]
return Object.keys(obj).every(function (k) {
return env[k] === obj[k]
})
}

View File

@@ -0,0 +1,36 @@
{
"name": "ci-info",
"version": "2.0.0",
"description": "Get details about the current Continuous Integration environment",
"main": "index.js",
"dependencies": {},
"devDependencies": {
"clear-require": "^1.0.1",
"standard": "^12.0.1",
"tape": "^4.9.1"
},
"scripts": {
"test": "standard && node test.js"
},
"repository": {
"type": "git",
"url": "https://github.com/watson/ci-info.git"
},
"keywords": [
"ci",
"continuous",
"integration",
"test",
"detect"
],
"author": "Thomas Watson Steen <w@tson.dk> (https://twitter.com/wa7son)",
"license": "MIT",
"bugs": {
"url": "https://github.com/watson/ci-info/issues"
},
"homepage": "https://github.com/watson/ci-info",
"coordinates": [
55.778231,
12.593179
]
}

View File

@@ -0,0 +1,153 @@
[
{
"name": "AppVeyor",
"constant": "APPVEYOR",
"env": "APPVEYOR",
"pr": "APPVEYOR_PULL_REQUEST_NUMBER"
},
{
"name": "Azure Pipelines",
"constant": "AZURE_PIPELINES",
"env": "SYSTEM_TEAMFOUNDATIONCOLLECTIONURI",
"pr": "SYSTEM_PULLREQUEST_PULLREQUESTID"
},
{
"name": "Bamboo",
"constant": "BAMBOO",
"env": "bamboo_planKey"
},
{
"name": "Bitbucket Pipelines",
"constant": "BITBUCKET",
"env": "BITBUCKET_COMMIT",
"pr": "BITBUCKET_PR_ID"
},
{
"name": "Bitrise",
"constant": "BITRISE",
"env": "BITRISE_IO",
"pr": "BITRISE_PULL_REQUEST"
},
{
"name": "Buddy",
"constant": "BUDDY",
"env": "BUDDY_WORKSPACE_ID",
"pr": "BUDDY_EXECUTION_PULL_REQUEST_ID"
},
{
"name": "Buildkite",
"constant": "BUILDKITE",
"env": "BUILDKITE",
"pr": { "env": "BUILDKITE_PULL_REQUEST", "ne": "false" }
},
{
"name": "CircleCI",
"constant": "CIRCLE",
"env": "CIRCLECI",
"pr": "CIRCLE_PULL_REQUEST"
},
{
"name": "Cirrus CI",
"constant": "CIRRUS",
"env": "CIRRUS_CI",
"pr": "CIRRUS_PR"
},
{
"name": "AWS CodeBuild",
"constant": "CODEBUILD",
"env": "CODEBUILD_BUILD_ARN"
},
{
"name": "Codeship",
"constant": "CODESHIP",
"env": { "CI_NAME": "codeship" }
},
{
"name": "Drone",
"constant": "DRONE",
"env": "DRONE",
"pr": { "DRONE_BUILD_EVENT": "pull_request" }
},
{
"name": "dsari",
"constant": "DSARI",
"env": "DSARI"
},
{
"name": "GitLab CI",
"constant": "GITLAB",
"env": "GITLAB_CI"
},
{
"name": "GoCD",
"constant": "GOCD",
"env": "GO_PIPELINE_LABEL"
},
{
"name": "Hudson",
"constant": "HUDSON",
"env": "HUDSON_URL"
},
{
"name": "Jenkins",
"constant": "JENKINS",
"env": ["JENKINS_URL", "BUILD_ID"],
"pr": { "any": ["ghprbPullId", "CHANGE_ID"] }
},
{
"name": "Magnum CI",
"constant": "MAGNUM",
"env": "MAGNUM"
},
{
"name": "Netlify CI",
"constant": "NETLIFY",
"env": "NETLIFY_BUILD_BASE",
"pr": { "env": "PULL_REQUEST", "ne": "false" }
},
{
"name": "Sail CI",
"constant": "SAIL",
"env": "SAILCI",
"pr": "SAIL_PULL_REQUEST_NUMBER"
},
{
"name": "Semaphore",
"constant": "SEMAPHORE",
"env": "SEMAPHORE",
"pr": "PULL_REQUEST_NUMBER"
},
{
"name": "Shippable",
"constant": "SHIPPABLE",
"env": "SHIPPABLE",
"pr": { "IS_PULL_REQUEST": "true" }
},
{
"name": "Solano CI",
"constant": "SOLANO",
"env": "TDDIUM",
"pr": "TDDIUM_PR_ID"
},
{
"name": "Strider CD",
"constant": "STRIDER",
"env": "STRIDER"
},
{
"name": "TaskCluster",
"constant": "TASKCLUSTER",
"env": ["TASK_ID", "RUN_ID"]
},
{
"name": "TeamCity",
"constant": "TEAMCITY",
"env": "TEAMCITY_VERSION"
},
{
"name": "Travis CI",
"constant": "TRAVIS",
"env": "TRAVIS",
"pr": { "env": "TRAVIS_PULL_REQUEST", "ne": "false" }
}
]

View File

@@ -0,0 +1 @@
repo_token: SIAeZjKYlHK74rbcFvNHMUzjRiMpflxve

View File

@@ -0,0 +1,11 @@
{
"env": {
"browser": true,
"node": true
},
"rules": {
"no-console": 0,
"no-empty": [1, { "allowEmptyCatch": true }]
},
"extends": "eslint:recommended"
}

View File

@@ -0,0 +1,9 @@
support
test
examples
example
*.sock
dist
yarn.lock
coverage
bower.json

View File

@@ -0,0 +1,14 @@
language: node_js
node_js:
- "6"
- "5"
- "4"
install:
- make node_modules
script:
- make lint
- make test
- make coveralls

View File

@@ -0,0 +1,362 @@
2.6.9 / 2017-09-22
==================
* remove ReDoS regexp in %o formatter (#504)
2.6.8 / 2017-05-18
==================
* Fix: Check for undefined on browser globals (#462, @marbemac)
2.6.7 / 2017-05-16
==================
* Fix: Update ms to 2.0.0 to fix regular expression denial of service vulnerability (#458, @hubdotcom)
* Fix: Inline extend function in node implementation (#452, @dougwilson)
* Docs: Fix typo (#455, @msasad)
2.6.5 / 2017-04-27
==================
* Fix: null reference check on window.documentElement.style.WebkitAppearance (#447, @thebigredgeek)
* Misc: clean up browser reference checks (#447, @thebigredgeek)
* Misc: add npm-debug.log to .gitignore (@thebigredgeek)
2.6.4 / 2017-04-20
==================
* Fix: bug that would occure if process.env.DEBUG is a non-string value. (#444, @LucianBuzzo)
* Chore: ignore bower.json in npm installations. (#437, @joaovieira)
* Misc: update "ms" to v0.7.3 (@tootallnate)
2.6.3 / 2017-03-13
==================
* Fix: Electron reference to `process.env.DEBUG` (#431, @paulcbetts)
* Docs: Changelog fix (@thebigredgeek)
2.6.2 / 2017-03-10
==================
* Fix: DEBUG_MAX_ARRAY_LENGTH (#420, @slavaGanzin)
* Docs: Add backers and sponsors from Open Collective (#422, @piamancini)
* Docs: Add Slackin invite badge (@tootallnate)
2.6.1 / 2017-02-10
==================
* Fix: Module's `export default` syntax fix for IE8 `Expected identifier` error
* Fix: Whitelist DEBUG_FD for values 1 and 2 only (#415, @pi0)
* Fix: IE8 "Expected identifier" error (#414, @vgoma)
* Fix: Namespaces would not disable once enabled (#409, @musikov)
2.6.0 / 2016-12-28
==================
* Fix: added better null pointer checks for browser useColors (@thebigredgeek)
* Improvement: removed explicit `window.debug` export (#404, @tootallnate)
* Improvement: deprecated `DEBUG_FD` environment variable (#405, @tootallnate)
2.5.2 / 2016-12-25
==================
* Fix: reference error on window within webworkers (#393, @KlausTrainer)
* Docs: fixed README typo (#391, @lurch)
* Docs: added notice about v3 api discussion (@thebigredgeek)
2.5.1 / 2016-12-20
==================
* Fix: babel-core compatibility
2.5.0 / 2016-12-20
==================
* Fix: wrong reference in bower file (@thebigredgeek)
* Fix: webworker compatibility (@thebigredgeek)
* Fix: output formatting issue (#388, @kribblo)
* Fix: babel-loader compatibility (#383, @escwald)
* Misc: removed built asset from repo and publications (@thebigredgeek)
* Misc: moved source files to /src (#378, @yamikuronue)
* Test: added karma integration and replaced babel with browserify for browser tests (#378, @yamikuronue)
* Test: coveralls integration (#378, @yamikuronue)
* Docs: simplified language in the opening paragraph (#373, @yamikuronue)
2.4.5 / 2016-12-17
==================
* Fix: `navigator` undefined in Rhino (#376, @jochenberger)
* Fix: custom log function (#379, @hsiliev)
* Improvement: bit of cleanup + linting fixes (@thebigredgeek)
* Improvement: rm non-maintainted `dist/` dir (#375, @freewil)
* Docs: simplified language in the opening paragraph. (#373, @yamikuronue)
2.4.4 / 2016-12-14
==================
* Fix: work around debug being loaded in preload scripts for electron (#368, @paulcbetts)
2.4.3 / 2016-12-14
==================
* Fix: navigation.userAgent error for react native (#364, @escwald)
2.4.2 / 2016-12-14
==================
* Fix: browser colors (#367, @tootallnate)
* Misc: travis ci integration (@thebigredgeek)
* Misc: added linting and testing boilerplate with sanity check (@thebigredgeek)
2.4.1 / 2016-12-13
==================
* Fix: typo that broke the package (#356)
2.4.0 / 2016-12-13
==================
* Fix: bower.json references unbuilt src entry point (#342, @justmatt)
* Fix: revert "handle regex special characters" (@tootallnate)
* Feature: configurable util.inspect()`options for NodeJS (#327, @tootallnate)
* Feature: %O`(big O) pretty-prints objects (#322, @tootallnate)
* Improvement: allow colors in workers (#335, @botverse)
* Improvement: use same color for same namespace. (#338, @lchenay)
2.3.3 / 2016-11-09
==================
* Fix: Catch `JSON.stringify()` errors (#195, Jovan Alleyne)
* Fix: Returning `localStorage` saved values (#331, Levi Thomason)
* Improvement: Don't create an empty object when no `process` (Nathan Rajlich)
2.3.2 / 2016-11-09
==================
* Fix: be super-safe in index.js as well (@TooTallNate)
* Fix: should check whether process exists (Tom Newby)
2.3.1 / 2016-11-09
==================
* Fix: Added electron compatibility (#324, @paulcbetts)
* Improvement: Added performance optimizations (@tootallnate)
* Readme: Corrected PowerShell environment variable example (#252, @gimre)
* Misc: Removed yarn lock file from source control (#321, @fengmk2)
2.3.0 / 2016-11-07
==================
* Fix: Consistent placement of ms diff at end of output (#215, @gorangajic)
* Fix: Escaping of regex special characters in namespace strings (#250, @zacronos)
* Fix: Fixed bug causing crash on react-native (#282, @vkarpov15)
* Feature: Enabled ES6+ compatible import via default export (#212 @bucaran)
* Feature: Added %O formatter to reflect Chrome's console.log capability (#279, @oncletom)
* Package: Update "ms" to 0.7.2 (#315, @DevSide)
* Package: removed superfluous version property from bower.json (#207 @kkirsche)
* Readme: fix USE_COLORS to DEBUG_COLORS
* Readme: Doc fixes for format string sugar (#269, @mlucool)
* Readme: Updated docs for DEBUG_FD and DEBUG_COLORS environment variables (#232, @mattlyons0)
* Readme: doc fixes for PowerShell (#271 #243, @exoticknight @unreadable)
* Readme: better docs for browser support (#224, @matthewmueller)
* Tooling: Added yarn integration for development (#317, @thebigredgeek)
* Misc: Renamed History.md to CHANGELOG.md (@thebigredgeek)
* Misc: Added license file (#226 #274, @CantemoInternal @sdaitzman)
* Misc: Updated contributors (@thebigredgeek)
2.2.0 / 2015-05-09
==================
* package: update "ms" to v0.7.1 (#202, @dougwilson)
* README: add logging to file example (#193, @DanielOchoa)
* README: fixed a typo (#191, @amir-s)
* browser: expose `storage` (#190, @stephenmathieson)
* Makefile: add a `distclean` target (#189, @stephenmathieson)
2.1.3 / 2015-03-13
==================
* Updated stdout/stderr example (#186)
* Updated example/stdout.js to match debug current behaviour
* Renamed example/stderr.js to stdout.js
* Update Readme.md (#184)
* replace high intensity foreground color for bold (#182, #183)
2.1.2 / 2015-03-01
==================
* dist: recompile
* update "ms" to v0.7.0
* package: update "browserify" to v9.0.3
* component: fix "ms.js" repo location
* changed bower package name
* updated documentation about using debug in a browser
* fix: security error on safari (#167, #168, @yields)
2.1.1 / 2014-12-29
==================
* browser: use `typeof` to check for `console` existence
* browser: check for `console.log` truthiness (fix IE 8/9)
* browser: add support for Chrome apps
* Readme: added Windows usage remarks
* Add `bower.json` to properly support bower install
2.1.0 / 2014-10-15
==================
* node: implement `DEBUG_FD` env variable support
* package: update "browserify" to v6.1.0
* package: add "license" field to package.json (#135, @panuhorsmalahti)
2.0.0 / 2014-09-01
==================
* package: update "browserify" to v5.11.0
* node: use stderr rather than stdout for logging (#29, @stephenmathieson)
1.0.4 / 2014-07-15
==================
* dist: recompile
* example: remove `console.info()` log usage
* example: add "Content-Type" UTF-8 header to browser example
* browser: place %c marker after the space character
* browser: reset the "content" color via `color: inherit`
* browser: add colors support for Firefox >= v31
* debug: prefer an instance `log()` function over the global one (#119)
* Readme: update documentation about styled console logs for FF v31 (#116, @wryk)
1.0.3 / 2014-07-09
==================
* Add support for multiple wildcards in namespaces (#122, @seegno)
* browser: fix lint
1.0.2 / 2014-06-10
==================
* browser: update color palette (#113, @gscottolson)
* common: make console logging function configurable (#108, @timoxley)
* node: fix %o colors on old node <= 0.8.x
* Makefile: find node path using shell/which (#109, @timoxley)
1.0.1 / 2014-06-06
==================
* browser: use `removeItem()` to clear localStorage
* browser, node: don't set DEBUG if namespaces is undefined (#107, @leedm777)
* package: add "contributors" section
* node: fix comment typo
* README: list authors
1.0.0 / 2014-06-04
==================
* make ms diff be global, not be scope
* debug: ignore empty strings in enable()
* node: make DEBUG_COLORS able to disable coloring
* *: export the `colors` array
* npmignore: don't publish the `dist` dir
* Makefile: refactor to use browserify
* package: add "browserify" as a dev dependency
* Readme: add Web Inspector Colors section
* node: reset terminal color for the debug content
* node: map "%o" to `util.inspect()`
* browser: map "%j" to `JSON.stringify()`
* debug: add custom "formatters"
* debug: use "ms" module for humanizing the diff
* Readme: add "bash" syntax highlighting
* browser: add Firebug color support
* browser: add colors for WebKit browsers
* node: apply log to `console`
* rewrite: abstract common logic for Node & browsers
* add .jshintrc file
0.8.1 / 2014-04-14
==================
* package: re-add the "component" section
0.8.0 / 2014-03-30
==================
* add `enable()` method for nodejs. Closes #27
* change from stderr to stdout
* remove unnecessary index.js file
0.7.4 / 2013-11-13
==================
* remove "browserify" key from package.json (fixes something in browserify)
0.7.3 / 2013-10-30
==================
* fix: catch localStorage security error when cookies are blocked (Chrome)
* add debug(err) support. Closes #46
* add .browser prop to package.json. Closes #42
0.7.2 / 2013-02-06
==================
* fix package.json
* fix: Mobile Safari (private mode) is broken with debug
* fix: Use unicode to send escape character to shell instead of octal to work with strict mode javascript
0.7.1 / 2013-02-05
==================
* add repository URL to package.json
* add DEBUG_COLORED to force colored output
* add browserify support
* fix component. Closes #24
0.7.0 / 2012-05-04
==================
* Added .component to package.json
* Added debug.component.js build
0.6.0 / 2012-03-16
==================
* Added support for "-" prefix in DEBUG [Vinay Pulim]
* Added `.enabled` flag to the node version [TooTallNate]
0.5.0 / 2012-02-02
==================
* Added: humanize diffs. Closes #8
* Added `debug.disable()` to the CS variant
* Removed padding. Closes #10
* Fixed: persist client-side variant again. Closes #9
0.4.0 / 2012-02-01
==================
* Added browser variant support for older browsers [TooTallNate]
* Added `debug.enable('project:*')` to browser variant [TooTallNate]
* Added padding to diff (moved it to the right)
0.3.0 / 2012-01-26
==================
* Added millisecond diff when isatty, otherwise UTC string
0.2.0 / 2012-01-22
==================
* Added wildcard support
0.1.0 / 2011-12-02
==================
* Added: remove colors unless stderr isatty [TooTallNate]
0.0.1 / 2010-01-03
==================
* Initial release

View File

@@ -0,0 +1,19 @@
(The MIT License)
Copyright (c) 2014 TJ Holowaychuk <tj@vision-media.ca>
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the 'Software'), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial
portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,50 @@
# get Makefile directory name: http://stackoverflow.com/a/5982798/376773
THIS_MAKEFILE_PATH:=$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
THIS_DIR:=$(shell cd $(dir $(THIS_MAKEFILE_PATH));pwd)
# BIN directory
BIN := $(THIS_DIR)/node_modules/.bin
# Path
PATH := node_modules/.bin:$(PATH)
SHELL := /bin/bash
# applications
NODE ?= $(shell which node)
YARN ?= $(shell which yarn)
PKG ?= $(if $(YARN),$(YARN),$(NODE) $(shell which npm))
BROWSERIFY ?= $(NODE) $(BIN)/browserify
.FORCE:
install: node_modules
node_modules: package.json
@NODE_ENV= $(PKG) install
@touch node_modules
lint: .FORCE
eslint browser.js debug.js index.js node.js
test-node: .FORCE
istanbul cover node_modules/mocha/bin/_mocha -- test/**.js
test-browser: .FORCE
mkdir -p dist
@$(BROWSERIFY) \
--standalone debug \
. > dist/debug.js
karma start --single-run
rimraf dist
test: .FORCE
concurrently \
"make test-node" \
"make test-browser"
coveralls:
cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js
.PHONY: all install clean distclean

View File

@@ -0,0 +1,312 @@
# debug
[![Build Status](https://travis-ci.org/visionmedia/debug.svg?branch=master)](https://travis-ci.org/visionmedia/debug) [![Coverage Status](https://coveralls.io/repos/github/visionmedia/debug/badge.svg?branch=master)](https://coveralls.io/github/visionmedia/debug?branch=master) [![Slack](https://visionmedia-community-slackin.now.sh/badge.svg)](https://visionmedia-community-slackin.now.sh/) [![OpenCollective](https://opencollective.com/debug/backers/badge.svg)](#backers)
[![OpenCollective](https://opencollective.com/debug/sponsors/badge.svg)](#sponsors)
A tiny node.js debugging utility modelled after node core's debugging technique.
**Discussion around the V3 API is under way [here](https://github.com/visionmedia/debug/issues/370)**
## Installation
```bash
$ npm install debug
```
## Usage
`debug` exposes a function; simply pass this function the name of your module, and it will return a decorated version of `console.error` for you to pass debug statements to. This will allow you to toggle the debug output for different parts of your module as well as the module as a whole.
Example _app.js_:
```js
var debug = require('debug')('http')
, http = require('http')
, name = 'My App';
// fake app
debug('booting %s', name);
http.createServer(function(req, res){
debug(req.method + ' ' + req.url);
res.end('hello\n');
}).listen(3000, function(){
debug('listening');
});
// fake worker of some kind
require('./worker');
```
Example _worker.js_:
```js
var debug = require('debug')('worker');
setInterval(function(){
debug('doing some work');
}, 1000);
```
The __DEBUG__ environment variable is then used to enable these based on space or comma-delimited names. Here are some examples:
![debug http and worker](http://f.cl.ly/items/18471z1H402O24072r1J/Screenshot.png)
![debug worker](http://f.cl.ly/items/1X413v1a3M0d3C2c1E0i/Screenshot.png)
#### Windows note
On Windows the environment variable is set using the `set` command.
```cmd
set DEBUG=*,-not_this
```
Note that PowerShell uses different syntax to set environment variables.
```cmd
$env:DEBUG = "*,-not_this"
```
Then, run the program to be debugged as usual.
## Millisecond diff
When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls.
![](http://f.cl.ly/items/2i3h1d3t121M2Z1A3Q0N/Screenshot.png)
When stdout is not a TTY, `Date#toUTCString()` is used, making it more useful for logging the debug information as shown below:
![](http://f.cl.ly/items/112H3i0e0o0P0a2Q2r11/Screenshot.png)
## Conventions
If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use ":" to separate features. For example "bodyParser" from Connect would then be "connect:bodyParser".
## Wildcards
The `*` character may be used as a wildcard. Suppose for example your library has debuggers named "connect:bodyParser", "connect:compress", "connect:session", instead of listing all three with `DEBUG=connect:bodyParser,connect:compress,connect:session`, you may simply do `DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`.
You can also exclude specific debuggers by prefixing them with a "-" character. For example, `DEBUG=*,-connect:*` would include all debuggers except those starting with "connect:".
## Environment Variables
When running through Node.js, you can set a few environment variables that will
change the behavior of the debug logging:
| Name | Purpose |
|-----------|-------------------------------------------------|
| `DEBUG` | Enables/disables specific debugging namespaces. |
| `DEBUG_COLORS`| Whether or not to use colors in the debug output. |
| `DEBUG_DEPTH` | Object inspection depth. |
| `DEBUG_SHOW_HIDDEN` | Shows hidden properties on inspected objects. |
__Note:__ The environment variables beginning with `DEBUG_` end up being
converted into an Options object that gets used with `%o`/`%O` formatters.
See the Node.js documentation for
[`util.inspect()`](https://nodejs.org/api/util.html#util_util_inspect_object_options)
for the complete list.
## Formatters
Debug uses [printf-style](https://wikipedia.org/wiki/Printf_format_string) formatting. Below are the officially supported formatters:
| Formatter | Representation |
|-----------|----------------|
| `%O` | Pretty-print an Object on multiple lines. |
| `%o` | Pretty-print an Object all on a single line. |
| `%s` | String. |
| `%d` | Number (both integer and float). |
| `%j` | JSON. Replaced with the string '[Circular]' if the argument contains circular references. |
| `%%` | Single percent sign ('%'). This does not consume an argument. |
### Custom formatters
You can add custom formatters by extending the `debug.formatters` object. For example, if you wanted to add support for rendering a Buffer as hex with `%h`, you could do something like:
```js
const createDebug = require('debug')
createDebug.formatters.h = (v) => {
return v.toString('hex')
}
// …elsewhere
const debug = createDebug('foo')
debug('this is hex: %h', new Buffer('hello world'))
// foo this is hex: 68656c6c6f20776f726c6421 +0ms
```
## Browser support
You can build a browser-ready script using [browserify](https://github.com/substack/node-browserify),
or just use the [browserify-as-a-service](https://wzrd.in/) [build](https://wzrd.in/standalone/debug@latest),
if you don't want to build it yourself.
Debug's enable state is currently persisted by `localStorage`.
Consider the situation shown below where you have `worker:a` and `worker:b`,
and wish to debug both. You can enable this using `localStorage.debug`:
```js
localStorage.debug = 'worker:*'
```
And then refresh the page.
```js
a = debug('worker:a');
b = debug('worker:b');
setInterval(function(){
a('doing some work');
}, 1000);
setInterval(function(){
b('doing some work');
}, 1200);
```
#### Web Inspector Colors
Colors are also enabled on "Web Inspectors" that understand the `%c` formatting
option. These are WebKit web inspectors, Firefox ([since version
31](https://hacks.mozilla.org/2014/05/editable-box-model-multiple-selection-sublime-text-keys-much-more-firefox-developer-tools-episode-31/))
and the Firebug plugin for Firefox (any version).
Colored output looks something like:
![](https://cloud.githubusercontent.com/assets/71256/3139768/b98c5fd8-e8ef-11e3-862a-f7253b6f47c6.png)
## Output streams
By default `debug` will log to stderr, however this can be configured per-namespace by overriding the `log` method:
Example _stdout.js_:
```js
var debug = require('debug');
var error = debug('app:error');
// by default stderr is used
error('goes to stderr!');
var log = debug('app:log');
// set this namespace to log via console.log
log.log = console.log.bind(console); // don't forget to bind to console!
log('goes to stdout');
error('still goes to stderr!');
// set all output to go via console.info
// overrides all per-namespace log settings
debug.log = console.info.bind(console);
error('now goes to stdout via console.info');
log('still goes to stdout, but via console.info now');
```
## Authors
- TJ Holowaychuk
- Nathan Rajlich
- Andrew Rhyne
## Backers
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/debug#backer)]
<a href="https://opencollective.com/debug/backer/0/website" target="_blank"><img src="https://opencollective.com/debug/backer/0/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/1/website" target="_blank"><img src="https://opencollective.com/debug/backer/1/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/2/website" target="_blank"><img src="https://opencollective.com/debug/backer/2/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/3/website" target="_blank"><img src="https://opencollective.com/debug/backer/3/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/4/website" target="_blank"><img src="https://opencollective.com/debug/backer/4/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/5/website" target="_blank"><img src="https://opencollective.com/debug/backer/5/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/6/website" target="_blank"><img src="https://opencollective.com/debug/backer/6/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/7/website" target="_blank"><img src="https://opencollective.com/debug/backer/7/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/8/website" target="_blank"><img src="https://opencollective.com/debug/backer/8/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/9/website" target="_blank"><img src="https://opencollective.com/debug/backer/9/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/10/website" target="_blank"><img src="https://opencollective.com/debug/backer/10/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/11/website" target="_blank"><img src="https://opencollective.com/debug/backer/11/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/12/website" target="_blank"><img src="https://opencollective.com/debug/backer/12/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/13/website" target="_blank"><img src="https://opencollective.com/debug/backer/13/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/14/website" target="_blank"><img src="https://opencollective.com/debug/backer/14/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/15/website" target="_blank"><img src="https://opencollective.com/debug/backer/15/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/16/website" target="_blank"><img src="https://opencollective.com/debug/backer/16/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/17/website" target="_blank"><img src="https://opencollective.com/debug/backer/17/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/18/website" target="_blank"><img src="https://opencollective.com/debug/backer/18/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/19/website" target="_blank"><img src="https://opencollective.com/debug/backer/19/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/20/website" target="_blank"><img src="https://opencollective.com/debug/backer/20/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/21/website" target="_blank"><img src="https://opencollective.com/debug/backer/21/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/22/website" target="_blank"><img src="https://opencollective.com/debug/backer/22/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/23/website" target="_blank"><img src="https://opencollective.com/debug/backer/23/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/24/website" target="_blank"><img src="https://opencollective.com/debug/backer/24/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/25/website" target="_blank"><img src="https://opencollective.com/debug/backer/25/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/26/website" target="_blank"><img src="https://opencollective.com/debug/backer/26/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/27/website" target="_blank"><img src="https://opencollective.com/debug/backer/27/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/28/website" target="_blank"><img src="https://opencollective.com/debug/backer/28/avatar.svg"></a>
<a href="https://opencollective.com/debug/backer/29/website" target="_blank"><img src="https://opencollective.com/debug/backer/29/avatar.svg"></a>
## Sponsors
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/debug#sponsor)]
<a href="https://opencollective.com/debug/sponsor/0/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/0/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/1/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/1/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/2/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/2/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/3/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/3/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/4/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/4/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/5/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/5/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/6/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/6/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/7/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/7/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/8/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/8/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/9/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/9/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/10/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/10/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/11/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/11/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/12/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/12/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/13/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/13/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/14/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/14/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/15/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/15/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/16/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/16/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/17/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/17/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/18/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/18/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/19/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/19/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/20/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/20/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/21/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/21/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/22/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/22/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/23/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/23/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/24/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/24/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/25/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/25/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/26/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/26/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/27/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/27/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/28/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/28/avatar.svg"></a>
<a href="https://opencollective.com/debug/sponsor/29/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/29/avatar.svg"></a>
## License
(The MIT License)
Copyright (c) 2014-2016 TJ Holowaychuk &lt;tj@vision-media.ca&gt;
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@@ -0,0 +1,19 @@
{
"name": "debug",
"repo": "visionmedia/debug",
"description": "small debugging utility",
"version": "2.6.9",
"keywords": [
"debug",
"log",
"debugger"
],
"main": "src/browser.js",
"scripts": [
"src/browser.js",
"src/debug.js"
],
"dependencies": {
"rauchg/ms.js": "0.7.1"
}
}

View File

@@ -0,0 +1,70 @@
// Karma configuration
// Generated on Fri Dec 16 2016 13:09:51 GMT+0000 (UTC)
module.exports = function(config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: ['mocha', 'chai', 'sinon'],
// list of files / patterns to load in the browser
files: [
'dist/debug.js',
'test/*spec.js'
],
// list of files to exclude
exclude: [
'src/node.js'
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: true,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['PhantomJS'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false,
// Concurrency level
// how many browser should be started simultaneous
concurrency: Infinity
})
}

View File

@@ -0,0 +1 @@
module.exports = require('./src/node');

View File

@@ -0,0 +1,49 @@
{
"name": "debug",
"version": "2.6.9",
"repository": {
"type": "git",
"url": "git://github.com/visionmedia/debug.git"
},
"description": "small debugging utility",
"keywords": [
"debug",
"log",
"debugger"
],
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
"Nathan Rajlich <nathan@tootallnate.net> (http://n8.io)",
"Andrew Rhyne <rhyneandrew@gmail.com>"
],
"license": "MIT",
"dependencies": {
"ms": "2.0.0"
},
"devDependencies": {
"browserify": "9.0.3",
"chai": "^3.5.0",
"concurrently": "^3.1.0",
"coveralls": "^2.11.15",
"eslint": "^3.12.1",
"istanbul": "^0.4.5",
"karma": "^1.3.0",
"karma-chai": "^0.1.0",
"karma-mocha": "^1.3.0",
"karma-phantomjs-launcher": "^1.0.2",
"karma-sinon": "^1.0.5",
"mocha": "^3.2.0",
"mocha-lcov-reporter": "^1.2.0",
"rimraf": "^2.5.4",
"sinon": "^1.17.6",
"sinon-chai": "^2.8.0"
},
"main": "./src/index.js",
"browser": "./src/browser.js",
"component": {
"scripts": {
"debug/index.js": "browser.js",
"debug/debug.js": "debug.js"
}
}
}

View File

@@ -0,0 +1,185 @@
/**
* This is the web browser implementation of `debug()`.
*
* Expose `debug()` as the module.
*/
exports = module.exports = require('./debug');
exports.log = log;
exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
exports.storage = 'undefined' != typeof chrome
&& 'undefined' != typeof chrome.storage
? chrome.storage.local
: localstorage();
/**
* Colors.
*/
exports.colors = [
'lightseagreen',
'forestgreen',
'goldenrod',
'dodgerblue',
'darkorchid',
'crimson'
];
/**
* Currently only WebKit-based Web Inspectors, Firefox >= v31,
* and the Firebug extension (any Firefox version) are known
* to support "%c" CSS customizations.
*
* TODO: add a `localStorage` variable to explicitly enable/disable colors
*/
function useColors() {
// NB: In an Electron preload script, document will be defined but not fully
// initialized. Since we know we're in Chrome, we'll just detect this case
// explicitly
if (typeof window !== 'undefined' && window.process && window.process.type === 'renderer') {
return true;
}
// is webkit? http://stackoverflow.com/a/16459606/376773
// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
// is firebug? http://stackoverflow.com/a/398120/376773
(typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
// is firefox >= v31?
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||
// double check webkit in userAgent just in case we are in a worker
(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
}
/**
* Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
*/
exports.formatters.j = function(v) {
try {
return JSON.stringify(v);
} catch (err) {
return '[UnexpectedJSONParseError]: ' + err.message;
}
};
/**
* Colorize log arguments if enabled.
*
* @api public
*/
function formatArgs(args) {
var useColors = this.useColors;
args[0] = (useColors ? '%c' : '')
+ this.namespace
+ (useColors ? ' %c' : ' ')
+ args[0]
+ (useColors ? '%c ' : ' ')
+ '+' + exports.humanize(this.diff);
if (!useColors) return;
var c = 'color: ' + this.color;
args.splice(1, 0, c, 'color: inherit')
// the final "%c" is somewhat tricky, because there could be other
// arguments passed either before or after the %c, so we need to
// figure out the correct index to insert the CSS into
var index = 0;
var lastC = 0;
args[0].replace(/%[a-zA-Z%]/g, function(match) {
if ('%%' === match) return;
index++;
if ('%c' === match) {
// we only are interested in the *last* %c
// (the user may have provided their own)
lastC = index;
}
});
args.splice(lastC, 0, c);
}
/**
* Invokes `console.log()` when available.
* No-op when `console.log` is not a "function".
*
* @api public
*/
function log() {
// this hackery is required for IE8/9, where
// the `console.log` function doesn't have 'apply'
return 'object' === typeof console
&& console.log
&& Function.prototype.apply.call(console.log, console, arguments);
}
/**
* Save `namespaces`.
*
* @param {String} namespaces
* @api private
*/
function save(namespaces) {
try {
if (null == namespaces) {
exports.storage.removeItem('debug');
} else {
exports.storage.debug = namespaces;
}
} catch(e) {}
}
/**
* Load `namespaces`.
*
* @return {String} returns the previously persisted debug modes
* @api private
*/
function load() {
var r;
try {
r = exports.storage.debug;
} catch(e) {}
// If debug isn't set in LS, and we're in Electron, try to load $DEBUG
if (!r && typeof process !== 'undefined' && 'env' in process) {
r = process.env.DEBUG;
}
return r;
}
/**
* Enable namespaces listed in `localStorage.debug` initially.
*/
exports.enable(load());
/**
* Localstorage attempts to return the localstorage.
*
* This is necessary because safari throws
* when a user disables cookies/localstorage
* and you attempt to access it.
*
* @return {LocalStorage}
* @api private
*/
function localstorage() {
try {
return window.localStorage;
} catch (e) {}
}

View File

@@ -0,0 +1,202 @@
/**
* This is the common logic for both the Node.js and web browser
* implementations of `debug()`.
*
* Expose `debug()` as the module.
*/
exports = module.exports = createDebug.debug = createDebug['default'] = createDebug;
exports.coerce = coerce;
exports.disable = disable;
exports.enable = enable;
exports.enabled = enabled;
exports.humanize = require('ms');
/**
* The currently active debug mode names, and names to skip.
*/
exports.names = [];
exports.skips = [];
/**
* Map of special "%n" handling functions, for the debug "format" argument.
*
* Valid key names are a single, lower or upper-case letter, i.e. "n" and "N".
*/
exports.formatters = {};
/**
* Previous log timestamp.
*/
var prevTime;
/**
* Select a color.
* @param {String} namespace
* @return {Number}
* @api private
*/
function selectColor(namespace) {
var hash = 0, i;
for (i in namespace) {
hash = ((hash << 5) - hash) + namespace.charCodeAt(i);
hash |= 0; // Convert to 32bit integer
}
return exports.colors[Math.abs(hash) % exports.colors.length];
}
/**
* Create a debugger with the given `namespace`.
*
* @param {String} namespace
* @return {Function}
* @api public
*/
function createDebug(namespace) {
function debug() {
// disabled?
if (!debug.enabled) return;
var self = debug;
// set `diff` timestamp
var curr = +new Date();
var ms = curr - (prevTime || curr);
self.diff = ms;
self.prev = prevTime;
self.curr = curr;
prevTime = curr;
// turn the `arguments` into a proper Array
var args = new Array(arguments.length);
for (var i = 0; i < args.length; i++) {
args[i] = arguments[i];
}
args[0] = exports.coerce(args[0]);
if ('string' !== typeof args[0]) {
// anything else let's inspect with %O
args.unshift('%O');
}
// apply any `formatters` transformations
var index = 0;
args[0] = args[0].replace(/%([a-zA-Z%])/g, function(match, format) {
// if we encounter an escaped % then don't increase the array index
if (match === '%%') return match;
index++;
var formatter = exports.formatters[format];
if ('function' === typeof formatter) {
var val = args[index];
match = formatter.call(self, val);
// now we need to remove `args[index]` since it's inlined in the `format`
args.splice(index, 1);
index--;
}
return match;
});
// apply env-specific formatting (colors, etc.)
exports.formatArgs.call(self, args);
var logFn = debug.log || exports.log || console.log.bind(console);
logFn.apply(self, args);
}
debug.namespace = namespace;
debug.enabled = exports.enabled(namespace);
debug.useColors = exports.useColors();
debug.color = selectColor(namespace);
// env-specific initialization logic for debug instances
if ('function' === typeof exports.init) {
exports.init(debug);
}
return debug;
}
/**
* Enables a debug mode by namespaces. This can include modes
* separated by a colon and wildcards.
*
* @param {String} namespaces
* @api public
*/
function enable(namespaces) {
exports.save(namespaces);
exports.names = [];
exports.skips = [];
var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/);
var len = split.length;
for (var i = 0; i < len; i++) {
if (!split[i]) continue; // ignore empty strings
namespaces = split[i].replace(/\*/g, '.*?');
if (namespaces[0] === '-') {
exports.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));
} else {
exports.names.push(new RegExp('^' + namespaces + '$'));
}
}
}
/**
* Disable debug output.
*
* @api public
*/
function disable() {
exports.enable('');
}
/**
* Returns true if the given mode name is enabled, false otherwise.
*
* @param {String} name
* @return {Boolean}
* @api public
*/
function enabled(name) {
var i, len;
for (i = 0, len = exports.skips.length; i < len; i++) {
if (exports.skips[i].test(name)) {
return false;
}
}
for (i = 0, len = exports.names.length; i < len; i++) {
if (exports.names[i].test(name)) {
return true;
}
}
return false;
}
/**
* Coerce `val`.
*
* @param {Mixed} val
* @return {Mixed}
* @api private
*/
function coerce(val) {
if (val instanceof Error) return val.stack || val.message;
return val;
}

View File

@@ -0,0 +1,10 @@
/**
* Detect Electron renderer process, which is node, but we should
* treat as a browser.
*/
if (typeof process !== 'undefined' && process.type === 'renderer') {
module.exports = require('./browser.js');
} else {
module.exports = require('./node.js');
}

View File

@@ -0,0 +1,15 @@
module.exports = inspectorLog;
// black hole
const nullStream = new (require('stream').Writable)();
nullStream._write = () => {};
/**
* Outputs a `console.log()` to the Node.js Inspector console *only*.
*/
function inspectorLog() {
const stdout = console._stdout;
console._stdout = nullStream;
console.log.apply(console, arguments);
console._stdout = stdout;
}

View File

@@ -0,0 +1,248 @@
/**
* Module dependencies.
*/
var tty = require('tty');
var util = require('util');
/**
* This is the Node.js implementation of `debug()`.
*
* Expose `debug()` as the module.
*/
exports = module.exports = require('./debug');
exports.init = init;
exports.log = log;
exports.formatArgs = formatArgs;
exports.save = save;
exports.load = load;
exports.useColors = useColors;
/**
* Colors.
*/
exports.colors = [6, 2, 3, 4, 5, 1];
/**
* Build up the default `inspectOpts` object from the environment variables.
*
* $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js
*/
exports.inspectOpts = Object.keys(process.env).filter(function (key) {
return /^debug_/i.test(key);
}).reduce(function (obj, key) {
// camel-case
var prop = key
.substring(6)
.toLowerCase()
.replace(/_([a-z])/g, function (_, k) { return k.toUpperCase() });
// coerce string value into JS value
var val = process.env[key];
if (/^(yes|on|true|enabled)$/i.test(val)) val = true;
else if (/^(no|off|false|disabled)$/i.test(val)) val = false;
else if (val === 'null') val = null;
else val = Number(val);
obj[prop] = val;
return obj;
}, {});
/**
* The file descriptor to write the `debug()` calls to.
* Set the `DEBUG_FD` env variable to override with another value. i.e.:
*
* $ DEBUG_FD=3 node script.js 3>debug.log
*/
var fd = parseInt(process.env.DEBUG_FD, 10) || 2;
if (1 !== fd && 2 !== fd) {
util.deprecate(function(){}, 'except for stderr(2) and stdout(1), any other usage of DEBUG_FD is deprecated. Override debug.log if you want to use a different log function (https://git.io/debug_fd)')()
}
var stream = 1 === fd ? process.stdout :
2 === fd ? process.stderr :
createWritableStdioStream(fd);
/**
* Is stdout a TTY? Colored output is enabled when `true`.
*/
function useColors() {
return 'colors' in exports.inspectOpts
? Boolean(exports.inspectOpts.colors)
: tty.isatty(fd);
}
/**
* Map %o to `util.inspect()`, all on a single line.
*/
exports.formatters.o = function(v) {
this.inspectOpts.colors = this.useColors;
return util.inspect(v, this.inspectOpts)
.split('\n').map(function(str) {
return str.trim()
}).join(' ');
};
/**
* Map %o to `util.inspect()`, allowing multiple lines if needed.
*/
exports.formatters.O = function(v) {
this.inspectOpts.colors = this.useColors;
return util.inspect(v, this.inspectOpts);
};
/**
* Adds ANSI color escape codes if enabled.
*
* @api public
*/
function formatArgs(args) {
var name = this.namespace;
var useColors = this.useColors;
if (useColors) {
var c = this.color;
var prefix = ' \u001b[3' + c + ';1m' + name + ' ' + '\u001b[0m';
args[0] = prefix + args[0].split('\n').join('\n' + prefix);
args.push('\u001b[3' + c + 'm+' + exports.humanize(this.diff) + '\u001b[0m');
} else {
args[0] = new Date().toUTCString()
+ ' ' + name + ' ' + args[0];
}
}
/**
* Invokes `util.format()` with the specified arguments and writes to `stream`.
*/
function log() {
return stream.write(util.format.apply(util, arguments) + '\n');
}
/**
* Save `namespaces`.
*
* @param {String} namespaces
* @api private
*/
function save(namespaces) {
if (null == namespaces) {
// If you set a process.env field to null or undefined, it gets cast to the
// string 'null' or 'undefined'. Just delete instead.
delete process.env.DEBUG;
} else {
process.env.DEBUG = namespaces;
}
}
/**
* Load `namespaces`.
*
* @return {String} returns the previously persisted debug modes
* @api private
*/
function load() {
return process.env.DEBUG;
}
/**
* Copied from `node/src/node.js`.
*
* XXX: It's lame that node doesn't expose this API out-of-the-box. It also
* relies on the undocumented `tty_wrap.guessHandleType()` which is also lame.
*/
function createWritableStdioStream (fd) {
var stream;
var tty_wrap = process.binding('tty_wrap');
// Note stream._type is used for test-module-load-list.js
switch (tty_wrap.guessHandleType(fd)) {
case 'TTY':
stream = new tty.WriteStream(fd);
stream._type = 'tty';
// Hack to have stream not keep the event loop alive.
// See https://github.com/joyent/node/issues/1726
if (stream._handle && stream._handle.unref) {
stream._handle.unref();
}
break;
case 'FILE':
var fs = require('fs');
stream = new fs.SyncWriteStream(fd, { autoClose: false });
stream._type = 'fs';
break;
case 'PIPE':
case 'TCP':
var net = require('net');
stream = new net.Socket({
fd: fd,
readable: false,
writable: true
});
// FIXME Should probably have an option in net.Socket to create a
// stream from an existing fd which is writable only. But for now
// we'll just add this hack and set the `readable` member to false.
// Test: ./node test/fixtures/echo.js < /etc/passwd
stream.readable = false;
stream.read = null;
stream._type = 'pipe';
// FIXME Hack to have stream not keep the event loop alive.
// See https://github.com/joyent/node/issues/1726
if (stream._handle && stream._handle.unref) {
stream._handle.unref();
}
break;
default:
// Probably an error on in uv_guess_handle()
throw new Error('Implement me. Unknown stream file type!');
}
// For supporting legacy API we put the FD here.
stream.fd = fd;
stream._isStdio = true;
return stream;
}
/**
* Init logic for `debug` instances.
*
* Create a new `inspectOpts` object in case `useColors` is set
* differently for a particular `debug` instance.
*/
function init (debug) {
debug.inspectOpts = {};
var keys = Object.keys(exports.inspectOpts);
for (var i = 0; i < keys.length; i++) {
debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]];
}
}
/**
* Enable namespaces listed in `process.env.DEBUG` initially.
*/
exports.enable(load());

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,3 @@
# hermes-estree
Flow types for the Flow-ESTree spec produced by the hermes parser

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,22 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _predicates = require("./predicates");
Object.keys(_predicates).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (key in exports && exports[key] === _predicates[key]) return;
exports[key] = _predicates[key];
});

View File

@@ -0,0 +1,15 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
'use strict';
export type * from './selectors';
export type * from './types';
export * from './predicates';

View File

@@ -0,0 +1,250 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
*/
'use strict';
/*::
import type {
ESNode,
Token,
MostTokens,
BlockComment,
LineComment,
AFunction,
PropertyDefinition,
PropertyDefinitionWithNonComputedName,
MethodDefinition,
MethodDefinitionConstructor,
MethodDefinitionWithNonComputedName,
MemberExpression,
MemberExpressionWithNonComputedName,
ObjectPropertyWithShorthandStaticName,
ObjectPropertyWithNonShorthandStaticName,
DestructuringObjectPropertyWithShorthandStaticName,
DestructuringObjectPropertyWithNonShorthandStaticName,
ClassMember,
ClassDeclaration,
ClassExpression,
Literal,
BigIntLiteral,
BooleanLiteral,
NullLiteral,
NumericLiteral,
RegExpLiteral,
StringLiteral,
Identifier,
EnumDefaultedMember,
Expression,
Statement,
} from './types';
*/
Object.defineProperty(exports, "__esModule", {
value: true
});
var _exportNames = {
isClass: true,
isPropertyDefinitionWithNonComputedName: true,
isClassMember: true,
isClassMemberWithNonComputedName: true,
isComment: true,
isFunction: true,
isMethodDefinitionWithNonComputedName: true,
isMemberExpressionWithNonComputedProperty: true,
isOptionalMemberExpressionWithNonComputedProperty: true,
isObjectPropertyWithShorthand: true,
isObjectPropertyWithNonComputedName: true,
isBigIntLiteral: true,
isBooleanLiteral: true,
isNullLiteral: true,
isNumericLiteral: true,
isRegExpLiteral: true,
isStringLiteral: true,
isExpression: true,
isStatement: true
};
exports.isBigIntLiteral = isBigIntLiteral;
exports.isBooleanLiteral = isBooleanLiteral;
exports.isClass = isClass;
exports.isClassMember = isClassMember;
exports.isClassMemberWithNonComputedName = isClassMemberWithNonComputedName;
exports.isComment = isComment;
exports.isExpression = isExpression;
exports.isFunction = isFunction;
exports.isMemberExpressionWithNonComputedProperty = isMemberExpressionWithNonComputedProperty;
exports.isMethodDefinitionWithNonComputedName = isMethodDefinitionWithNonComputedName;
exports.isNullLiteral = isNullLiteral;
exports.isNumericLiteral = isNumericLiteral;
exports.isObjectPropertyWithNonComputedName = isObjectPropertyWithNonComputedName;
exports.isObjectPropertyWithShorthand = isObjectPropertyWithShorthand;
exports.isOptionalMemberExpressionWithNonComputedProperty = isOptionalMemberExpressionWithNonComputedProperty;
exports.isPropertyDefinitionWithNonComputedName = isPropertyDefinitionWithNonComputedName;
exports.isRegExpLiteral = isRegExpLiteral;
exports.isStatement = isStatement;
exports.isStringLiteral = isStringLiteral;
var _predicates = require("./generated/predicates");
Object.keys(_predicates).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _predicates[key]) return;
exports[key] = _predicates[key];
});
function isClass(node
/*: ESNode */
)
/*: implies node is (ClassDeclaration | ClassExpression) */
{
return node.type === 'ClassDeclaration' || node.type === 'ClassExpression';
}
function isPropertyDefinitionWithNonComputedName(node
/*: ESNode */
)
/*: implies node is PropertyDefinitionWithNonComputedName */
{
return node.type === 'PropertyDefinition' && node.computed === false;
}
function isClassMember(node
/*: ESNode */
)
/*: implies node is ClassMember */
{
return node.type === 'PropertyDefinition' || node.type === 'MethodDefinition';
}
function isClassMemberWithNonComputedName(node
/*: ESNode */
)
/*: implies node is (PropertyDefinitionWithNonComputedName | MethodDefinitionConstructor | MethodDefinitionWithNonComputedName) */
{
return (node.type === 'PropertyDefinition' || node.type === 'MethodDefinition') && node.computed === false;
}
function isComment(node
/*: ESNode | Token */
)
/*: implies node is (MostTokens | BlockComment | LineComment) */
{
return node.type === 'Block' || node.type === 'Line';
}
function isFunction(node
/*: ESNode */
)
/*: implies node is AFunction */
{
return node.type === 'ArrowFunctionExpression' || node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression';
}
function isMethodDefinitionWithNonComputedName(node
/*: ESNode */
)
/*: implies node is (MethodDefinitionConstructor | MethodDefinitionWithNonComputedName) */
{
return node.type === 'MethodDefinition' && node.computed === false;
}
function isMemberExpressionWithNonComputedProperty(node
/*: ESNode */
)
/*: implies node is MemberExpressionWithNonComputedName */
{
return node.type === 'MemberExpression' && node.computed === false;
}
function isOptionalMemberExpressionWithNonComputedProperty(node
/*: ESNode */
)
/*: implies node is MemberExpressionWithNonComputedName */
{
return node.type === 'MemberExpression' && node.computed === false;
}
function isObjectPropertyWithShorthand(node
/*: ESNode */
)
/*: implies node is (ObjectPropertyWithShorthandStaticName | DestructuringObjectPropertyWithShorthandStaticName) */
{
return node.type === 'Property' && node.shorthand === true;
}
function isObjectPropertyWithNonComputedName(node
/*: ESNode */
)
/*: implies node is (ObjectPropertyWithNonShorthandStaticName | ObjectPropertyWithShorthandStaticName | DestructuringObjectPropertyWithNonShorthandStaticName | DestructuringObjectPropertyWithShorthandStaticName) */
{
return node.type === 'Property' && node.computed === false;
}
function isBigIntLiteral(node
/*: ESNode */
)
/*: implies node is BigIntLiteral */
{
return node.type === 'Literal' && node.literalType === 'bigint';
}
function isBooleanLiteral(node
/*: ESNode */
)
/*: implies node is BooleanLiteral */
{
return node.type === 'Literal' && node.literalType === 'boolean';
}
function isNullLiteral(node
/*: ESNode */
)
/*: implies node is NullLiteral */
{
return node.type === 'Literal' && node.literalType === 'null';
}
function isNumericLiteral(node
/*: ESNode */
)
/*: implies node is NumericLiteral */
{
return node.type === 'Literal' && node.literalType === 'numeric';
}
function isRegExpLiteral(node
/*: ESNode */
)
/*: implies node is RegExpLiteral */
{
return node.type === 'Literal' && node.literalType === 'regexp';
}
function isStringLiteral(node
/*: ESNode */
)
/*: implies node is StringLiteral */
{
return node.type === 'Literal' && node.literalType === 'string';
}
function isExpression(node
/*: ESNode */
)
/*: implies node is Expression */
{
return node.type === 'ThisExpression' || node.type === 'ArrayExpression' || node.type === 'ObjectExpression' || // $FlowFixMe[incompatible-type]
node.type === 'ObjectExpression' || node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression' || node.type === 'YieldExpression' || node.type === 'Literal' || node.type === 'UnaryExpression' || node.type === 'UpdateExpression' || node.type === 'BinaryExpression' || node.type === 'AssignmentExpression' || node.type === 'LogicalExpression' || node.type === 'MemberExpression' || node.type === 'ConditionalExpression' || node.type === 'CallExpression' || node.type === 'NewExpression' || node.type === 'SequenceExpression' || node.type === 'TemplateLiteral' || node.type === 'TaggedTemplateExpression' || node.type === 'ClassExpression' || node.type === 'MetaProperty' || node.type === 'Identifier' || node.type === 'AwaitExpression' || node.type === 'ImportExpression' || node.type === 'ChainExpression' || node.type === 'TypeCastExpression' || node.type === 'AsExpression' || node.type === 'AsConstExpression' || node.type === 'JSXFragment' || node.type === 'JSXElement';
}
function isStatement(node
/*: ESNode */
)
/*: implies node is Statement */
{
return node.type === 'BlockStatement' || node.type === 'BreakStatement' || node.type === 'ClassDeclaration' || node.type === 'ContinueStatement' || node.type === 'DebuggerStatement' || node.type === 'DeclareClass' || node.type === 'DeclareVariable' || node.type === 'DeclareFunction' || node.type === 'DeclareInterface' || node.type === 'DeclareModule' || node.type === 'DeclareOpaqueType' || node.type === 'DeclareTypeAlias' || node.type === 'DoWhileStatement' || node.type === 'EmptyStatement' || node.type === 'EnumDeclaration' || node.type === 'ExpressionStatement' || node.type === 'ForInStatement' || node.type === 'ForOfStatement' || node.type === 'ForStatement' || node.type === 'FunctionDeclaration' || node.type === 'IfStatement' || node.type === 'InterfaceDeclaration' || node.type === 'LabeledStatement' || node.type === 'OpaqueType' || node.type === 'ReturnStatement' || node.type === 'SwitchStatement' || node.type === 'ThrowStatement' || node.type === 'TryStatement' || node.type === 'TypeAlias' || node.type === 'VariableDeclaration' || node.type === 'WhileStatement' || node.type === 'WithStatement';
}

View File

@@ -0,0 +1,204 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict-local
*/
'use strict';
/*::
import type {
ESNode,
Token,
MostTokens,
BlockComment,
LineComment,
AFunction,
PropertyDefinition,
PropertyDefinitionWithNonComputedName,
MethodDefinition,
MethodDefinitionConstructor,
MethodDefinitionWithNonComputedName,
MemberExpression,
MemberExpressionWithNonComputedName,
ObjectPropertyWithShorthandStaticName,
ObjectPropertyWithNonShorthandStaticName,
DestructuringObjectPropertyWithShorthandStaticName,
DestructuringObjectPropertyWithNonShorthandStaticName,
ClassMember,
ClassDeclaration,
ClassExpression,
Literal,
BigIntLiteral,
BooleanLiteral,
NullLiteral,
NumericLiteral,
RegExpLiteral,
StringLiteral,
Identifier,
EnumDefaultedMember,
Expression,
Statement,
} from './types';
*/
export * from './generated/predicates';
export function isClass(node /*: ESNode */) /*: implies node is (ClassDeclaration | ClassExpression) */ {
return node.type === 'ClassDeclaration' || node.type === 'ClassExpression';
}
export function isPropertyDefinitionWithNonComputedName(
node /*: ESNode */,
) /*: implies node is PropertyDefinitionWithNonComputedName */ {
return node.type === 'PropertyDefinition' && node.computed === false;
}
export function isClassMember(node /*: ESNode */) /*: implies node is ClassMember */ {
return node.type === 'PropertyDefinition' || node.type === 'MethodDefinition';
}
export function isClassMemberWithNonComputedName(
node /*: ESNode */,
) /*: implies node is (PropertyDefinitionWithNonComputedName | MethodDefinitionConstructor | MethodDefinitionWithNonComputedName) */ {
return (node.type === 'PropertyDefinition' || node.type === 'MethodDefinition') && node.computed === false;
}
export function isComment(node /*: ESNode | Token */) /*: implies node is (MostTokens | BlockComment | LineComment) */ {
return node.type === 'Block' || node.type === 'Line';
}
export function isFunction(node /*: ESNode */) /*: implies node is AFunction */ {
return (
node.type === 'ArrowFunctionExpression' ||
node.type === 'FunctionDeclaration' ||
node.type === 'FunctionExpression'
);
}
export function isMethodDefinitionWithNonComputedName(
node /*: ESNode */,
) /*: implies node is (MethodDefinitionConstructor | MethodDefinitionWithNonComputedName) */ {
return node.type === 'MethodDefinition' && node.computed === false;
}
export function isMemberExpressionWithNonComputedProperty(
node /*: ESNode */,
) /*: implies node is MemberExpressionWithNonComputedName */ {
return node.type === 'MemberExpression' && node.computed === false;
}
export function isOptionalMemberExpressionWithNonComputedProperty(
node /*: ESNode */,
) /*: implies node is MemberExpressionWithNonComputedName */ {
return node.type === 'MemberExpression' && node.computed === false;
}
export function isObjectPropertyWithShorthand(node /*: ESNode */) /*: implies node is (ObjectPropertyWithShorthandStaticName | DestructuringObjectPropertyWithShorthandStaticName) */ {
return node.type === 'Property' && node.shorthand === true;
}
export function isObjectPropertyWithNonComputedName(node /*: ESNode */) /*: implies node is (ObjectPropertyWithNonShorthandStaticName | ObjectPropertyWithShorthandStaticName | DestructuringObjectPropertyWithNonShorthandStaticName | DestructuringObjectPropertyWithShorthandStaticName) */ {
return node.type === 'Property' && node.computed === false;
}
export function isBigIntLiteral(node /*: ESNode */) /*: implies node is BigIntLiteral */ {
return node.type === 'Literal' && node.literalType === 'bigint';
}
export function isBooleanLiteral(node /*: ESNode */) /*: implies node is BooleanLiteral */ {
return node.type === 'Literal' && node.literalType === 'boolean';
}
export function isNullLiteral(node /*: ESNode */) /*: implies node is NullLiteral */ {
return node.type === 'Literal' && node.literalType === 'null';
}
export function isNumericLiteral(node /*: ESNode */) /*: implies node is NumericLiteral */ {
return node.type === 'Literal' && node.literalType === 'numeric';
}
export function isRegExpLiteral(node /*: ESNode */) /*: implies node is RegExpLiteral */ {
return node.type === 'Literal' && node.literalType === 'regexp';
}
export function isStringLiteral(node /*: ESNode */) /*: implies node is StringLiteral */ {
return node.type === 'Literal' && node.literalType === 'string';
}
export function isExpression(node /*: ESNode */) /*: implies node is Expression */ {
return (
node.type === 'ThisExpression' ||
node.type === 'ArrayExpression' ||
node.type === 'ObjectExpression' ||
// $FlowFixMe[incompatible-type]
node.type === 'ObjectExpression' ||
node.type === 'FunctionExpression' ||
node.type === 'ArrowFunctionExpression' ||
node.type === 'YieldExpression' ||
node.type === 'Literal' ||
node.type === 'UnaryExpression' ||
node.type === 'UpdateExpression' ||
node.type === 'BinaryExpression' ||
node.type === 'AssignmentExpression' ||
node.type === 'LogicalExpression' ||
node.type === 'MemberExpression' ||
node.type === 'ConditionalExpression' ||
node.type === 'CallExpression' ||
node.type === 'NewExpression' ||
node.type === 'SequenceExpression' ||
node.type === 'TemplateLiteral' ||
node.type === 'TaggedTemplateExpression' ||
node.type === 'ClassExpression' ||
node.type === 'MetaProperty' ||
node.type === 'Identifier' ||
node.type === 'AwaitExpression' ||
node.type === 'ImportExpression' ||
node.type === 'ChainExpression' ||
node.type === 'TypeCastExpression' ||
node.type === 'AsExpression' ||
node.type === 'AsConstExpression' ||
node.type === 'JSXFragment' ||
node.type === 'JSXElement'
);
}
export function isStatement(node /*: ESNode */) /*: implies node is Statement */ {
return (
node.type === 'BlockStatement' ||
node.type === 'BreakStatement' ||
node.type === 'ClassDeclaration' ||
node.type === 'ContinueStatement' ||
node.type === 'DebuggerStatement' ||
node.type === 'DeclareClass' ||
node.type === 'DeclareVariable' ||
node.type === 'DeclareFunction' ||
node.type === 'DeclareInterface' ||
node.type === 'DeclareModule' ||
node.type === 'DeclareOpaqueType' ||
node.type === 'DeclareTypeAlias' ||
node.type === 'DoWhileStatement' ||
node.type === 'EmptyStatement' ||
node.type === 'EnumDeclaration' ||
node.type === 'ExpressionStatement' ||
node.type === 'ForInStatement' ||
node.type === 'ForOfStatement' ||
node.type === 'ForStatement' ||
node.type === 'FunctionDeclaration' ||
node.type === 'IfStatement' ||
node.type === 'InterfaceDeclaration' ||
node.type === 'LabeledStatement' ||
node.type === 'OpaqueType' ||
node.type === 'ReturnStatement' ||
node.type === 'SwitchStatement' ||
node.type === 'ThrowStatement' ||
node.type === 'TryStatement' ||
node.type === 'TypeAlias' ||
node.type === 'VariableDeclaration' ||
node.type === 'WhileStatement' ||
node.type === 'WithStatement'
);
}

View File

@@ -0,0 +1,14 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});

View File

@@ -0,0 +1,28 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
'use strict';
import type {ESQueryNodeSelectorsWithoutFallback} from './generated/HermesESTreeSelectorTypes';
export type ESQueryNodeSelectors = {
...ESQueryNodeSelectorsWithoutFallback,
// We want to allow consumers to manually type their weird selectors.
// If we use the \`ESNode\` type here then flow will error on cases like this:
// 'FunctionDeclaration[id="foo"]'(node: FunctionDeclaration) {...}
// But this sucks as it means someone would then have to manually do an \`if\`
// check inside the selector body.
+[selector: string]: (node: $FlowFixMe) => void,
};
export type {ESQueryNodeSelectorsWithoutFallback} from './generated/HermesESTreeSelectorTypes';
export {};

View File

@@ -0,0 +1,39 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
'use strict';
/**
*
* IMPORTANT NOTE
*
* This file intentionally uses interfaces and `+` for readonly.
*
* - `$ReadOnly` is an "evaluated" utility type in flow; meaning that flow does
* not actually calculate the resulting type until it is used. This creates
* a copy of the type at each usage site - ballooning memory and processing
* times.
* Usually this isn't a problem as a type might only be used one or two times
* - but in this giant circular-referencing graph that is the AST types, this
* causes check times for consumers to be awful.
*
* Thus instead we manually annotate properties with `+` to avoid the `$ReadOnly` type.
*
* - `...Type` spreads do not preserve the readonly-ness of the properties. If
* we used object literal types then we would have to `$ReadOnly` all spreads
* (see point 1). On the other hand extending an interface does preserve
* readonlyness of properties.
*
* Thus instead of object literals, we use interfaces.
*
*** Please ensure all properties are marked as readonly! ***
*/
Object.defineProperty(exports, "__esModule", {
value: true
});

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,16 @@
{
"name": "hermes-estree",
"version": "0.23.1",
"description": "Flow types for the Flow-ESTree spec produced by the hermes parser",
"main": "dist/index.js",
"license": "MIT",
"repository": {
"type": "git",
"url": "git@github.com:facebook/hermes.git"
},
"files": [
"dist",
"LICENCE",
"README.md"
]
}

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) Meta Platforms, Inc. and affiliates.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,11 @@
# hermes-parser
A JavaScript parser built from the Hermes engine's parser compiled to WebAssembly. Can parse ES6, Flow, and JSX syntax.
## API
The Hermes parser exposes a single `parse(code, [options])` function, where `code` is the source code to parse as a string, and `options` is an optional object that may contain the following properties:
- **babel**: `boolean`, defaults to `false`. If `true`, output an AST conforming to Babel's AST format. If `false`, output an AST conforming to the ESTree AST format.
- **allowReturnOutsideFunction**: `boolean`, defaults to `false`. If `true`, do not error on return statements found outside functions.
- **flow**: `"all"` or `"detect"`, defaults to `"detect"`. If `"detect"`, only parse syntax as Flow syntax where it is ambiguous whether it is a Flow feature or regular JavaScript when the `@flow` pragma is present in the file. Otherwise if `"all"`, always parse ambiguous syntax as Flow syntax regardless of the presence of an `@flow` pragma. For example `foo<T>(x)` in a file without an `@flow` pragma will be parsed as two comparisons if set to `"detect"`, otherwise if set to `"all"` or the `@flow` pragma is included it will be parsed as a call expression with a type argument.
- **sourceFilename**: `string`, defaults to `null`. The filename corresponding to the code that is to be parsed. If non-null, the filename will be added to all source locations in the output AST.
- **sourceType**: `"module"`, `"script"`, or `"unambiguous"` (default). If `"unambiguous"`, source type will be automatically detected and set to `"module"` if any ES6 imports or exports are present in the code, otherwise source type will be set to `"script"`.
- **tokens**: `boolean`, defaults to `false`. If `true`, add all tokens to a `tokens` property on the root node.

View File

@@ -0,0 +1,57 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
// flowlint unclear-type:off
export type HermesPosition = {
/** >= 1 */
+line: number,
/** >= 0 */
+column: number,
};
export type HermesSourceLocation = {
start?: HermesPosition,
end?: HermesPosition,
rangeStart?: number,
rangeEnd?: number,
};
export type HermesNode = {
type: string,
[string]: any,
};
export type HermesToken = {
type:
| 'Boolean'
| 'Identifier'
| 'Keyword'
| 'Null'
| 'Numeric'
| 'BigInt'
| 'Punctuator'
| 'String'
| 'RegularExpression'
| 'Template'
| 'JSXText',
loc: HermesSourceLocation,
value: ?string,
};
export type HermesComment = {
type: 'CommentLine' | 'CommentBlock' | 'InterpreterDirective',
loc: HermesSourceLocation,
value: ?string,
};
export type HermesProgram = {
type: 'Program',
loc: HermesSourceLocation,
body: Array<?HermesNode>,
comments: Array<HermesComment>,
tokens?: Array<HermesToken>,
interpreter?: ?HermesComment,
};

View File

@@ -0,0 +1,193 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _ParserVisitorKeys = require("./generated/ParserVisitorKeys");
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
/**
* The base class for transforming the Hermes AST to the desired output format.
* Extended by concrete adapters which output an ESTree or Babel AST.
*/
class HermesASTAdapter {
constructor(options) {
this.sourceFilename = void 0;
this.sourceType = void 0;
this.sourceFilename = options.sourceFilename;
this.sourceType = options.sourceType;
}
/**
* Transform the input Hermes AST to the desired output format.
* This modifies the input AST in place instead of constructing a new AST.
*/
transform(program) {
// Comments are not traversed via visitor keys
const comments = program.comments;
for (let i = 0; i < comments.length; i++) {
const comment = comments[i];
this.fixSourceLocation(comment);
comments[i] = this.mapComment(comment);
} // The first comment may be an interpreter directive and is stored directly on the program node
program.interpreter = comments.length > 0 && comments[0].type === 'InterpreterDirective' ? comments.shift() : null; // Tokens are not traversed via visitor keys
const tokens = program.tokens;
if (tokens) {
for (let i = 0; i < tokens.length; i++) {
this.fixSourceLocation(tokens[i]);
}
}
const resultNode = this.mapNode(program);
if (resultNode.type !== 'Program') {
throw new Error(`HermesToESTreeAdapter: Must return a Program node, instead of "${resultNode.type}". `);
} // $FlowExpectedError[incompatible-return] We know this is a program at this point.
return resultNode;
}
/**
* Transform a Hermes AST node to the output AST format.
*
* This may modify the input node in-place and return that same node, or a completely
* new node may be constructed and returned. Overriden in child classes.
*/
mapNode(_node) {
throw new Error('Implemented in subclasses');
}
mapNodeDefault(node) {
const visitorKeys = _ParserVisitorKeys.HERMES_AST_VISITOR_KEYS[node.type];
for (const key in visitorKeys) {
const childType = visitorKeys[key];
if (childType === _ParserVisitorKeys.NODE_CHILD) {
const child = node[key];
if (child != null) {
node[key] = this.mapNode(child);
}
} else if (childType === _ParserVisitorKeys.NODE_LIST_CHILD) {
const children = node[key];
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (child != null) {
children[i] = this.mapNode(child);
}
}
}
}
return node;
}
/**
* Update the source location for this node depending on the output AST format.
* This can modify the input node in-place. Overriden in child classes.
*/
fixSourceLocation(_node) {
throw new Error('Implemented in subclasses');
}
getSourceType() {
var _this$sourceType;
return (_this$sourceType = this.sourceType) != null ? _this$sourceType : 'script';
}
setModuleSourceType() {
if (this.sourceType == null) {
this.sourceType = 'module';
}
}
mapComment(node) {
return node;
}
mapEmpty(_node) {
// $FlowExpectedError
return null;
}
mapImportDeclaration(node) {
if (node.importKind === 'value') {
this.setModuleSourceType();
}
return this.mapNodeDefault(node);
}
mapImportSpecifier(node) {
if (node.importKind === 'value') {
node.importKind = null;
}
return this.mapNodeDefault(node);
}
mapExportDefaultDeclaration(node) {
this.setModuleSourceType();
return this.mapNodeDefault(node);
}
mapExportNamedDeclaration(node) {
if (node.exportKind === 'value') {
this.setModuleSourceType();
}
return this.mapNodeDefault(node);
}
mapExportAllDeclaration(node) {
if (node.exportKind === 'value') {
this.setModuleSourceType();
}
return this.mapNodeDefault(node);
}
formatError(node, message) {
return `${message} (${node.loc.start.line}:${node.loc.start.column})`;
}
getBigIntLiteralValue(bigintString) {
// TODO - once we update flow we can remove this
const bigint = bigintString // estree spec is to not have a trailing `n` on this property
// https://github.com/estree/estree/blob/db962bb417a97effcfe9892f87fbb93c81a68584/es2020.md#bigintliteral
.replace(/n$/, '') // `BigInt` doesn't accept numeric separator and `bigint` property should not include numeric separator
.replace(/_/, '');
return {
bigint,
// coerce the string to a bigint value if supported by the environment
value: typeof BigInt === 'function' ? BigInt(bigint) : null
};
}
}
exports.default = HermesASTAdapter;

View File

@@ -0,0 +1,192 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {Program} from 'hermes-estree';
import type {HermesNode} from './HermesAST';
import type {ParserOptions} from './ParserOptions';
import {
HERMES_AST_VISITOR_KEYS,
NODE_CHILD,
NODE_LIST_CHILD,
} from './generated/ParserVisitorKeys';
/**
* The base class for transforming the Hermes AST to the desired output format.
* Extended by concrete adapters which output an ESTree or Babel AST.
*/
export default class HermesASTAdapter {
sourceFilename: ParserOptions['sourceFilename'];
sourceType: ParserOptions['sourceType'];
constructor(options: ParserOptions) {
this.sourceFilename = options.sourceFilename;
this.sourceType = options.sourceType;
}
/**
* Transform the input Hermes AST to the desired output format.
* This modifies the input AST in place instead of constructing a new AST.
*/
transform(program: HermesNode): Program {
// Comments are not traversed via visitor keys
const comments = program.comments;
for (let i = 0; i < comments.length; i++) {
const comment = comments[i];
this.fixSourceLocation(comment);
comments[i] = this.mapComment(comment);
}
// The first comment may be an interpreter directive and is stored directly on the program node
program.interpreter =
comments.length > 0 && comments[0].type === 'InterpreterDirective'
? comments.shift()
: null;
// Tokens are not traversed via visitor keys
const tokens = program.tokens;
if (tokens) {
for (let i = 0; i < tokens.length; i++) {
this.fixSourceLocation(tokens[i]);
}
}
const resultNode = this.mapNode(program);
if (resultNode.type !== 'Program') {
throw new Error(
`HermesToESTreeAdapter: Must return a Program node, instead of "${resultNode.type}". `,
);
}
// $FlowExpectedError[incompatible-return] We know this is a program at this point.
return resultNode;
}
/**
* Transform a Hermes AST node to the output AST format.
*
* This may modify the input node in-place and return that same node, or a completely
* new node may be constructed and returned. Overriden in child classes.
*/
mapNode(_node: HermesNode): HermesNode {
throw new Error('Implemented in subclasses');
}
mapNodeDefault(node: HermesNode): HermesNode {
const visitorKeys = HERMES_AST_VISITOR_KEYS[node.type];
for (const key in visitorKeys) {
const childType = visitorKeys[key];
if (childType === NODE_CHILD) {
const child = node[key];
if (child != null) {
node[key] = this.mapNode(child);
}
} else if (childType === NODE_LIST_CHILD) {
const children = node[key];
for (let i = 0; i < children.length; i++) {
const child = children[i];
if (child != null) {
children[i] = this.mapNode(child);
}
}
}
}
return node;
}
/**
* Update the source location for this node depending on the output AST format.
* This can modify the input node in-place. Overriden in child classes.
*/
fixSourceLocation(_node: HermesNode): void {
throw new Error('Implemented in subclasses');
}
getSourceType(): ParserOptions['sourceType'] {
return this.sourceType ?? 'script';
}
setModuleSourceType(): void {
if (this.sourceType == null) {
this.sourceType = 'module';
}
}
mapComment(node: HermesNode): HermesNode {
return node;
}
mapEmpty(_node: HermesNode): HermesNode {
// $FlowExpectedError
return null;
}
mapImportDeclaration(node: HermesNode): HermesNode {
if (node.importKind === 'value') {
this.setModuleSourceType();
}
return this.mapNodeDefault(node);
}
mapImportSpecifier(node: HermesNode): HermesNode {
if (node.importKind === 'value') {
node.importKind = null;
}
return this.mapNodeDefault(node);
}
mapExportDefaultDeclaration(node: HermesNode): HermesNode {
this.setModuleSourceType();
return this.mapNodeDefault(node);
}
mapExportNamedDeclaration(node: HermesNode): HermesNode {
if (node.exportKind === 'value') {
this.setModuleSourceType();
}
return this.mapNodeDefault(node);
}
mapExportAllDeclaration(node: HermesNode): HermesNode {
if (node.exportKind === 'value') {
this.setModuleSourceType();
}
return this.mapNodeDefault(node);
}
formatError(node: HermesNode, message: string): string {
return `${message} (${node.loc.start.line}:${node.loc.start.column})`;
}
getBigIntLiteralValue(bigintString: string): {
bigint: string,
value: $FlowFixMe /* bigint */,
} {
// TODO - once we update flow we can remove this
declare var BigInt: ?(value: $FlowFixMe) => mixed;
const bigint = bigintString
// estree spec is to not have a trailing `n` on this property
// https://github.com/estree/estree/blob/db962bb417a97effcfe9892f87fbb93c81a68584/es2020.md#bigintliteral
.replace(/n$/, '')
// `BigInt` doesn't accept numeric separator and `bigint` property should not include numeric separator
.replace(/_/, '');
return {
bigint,
// coerce the string to a bigint value if supported by the environment
value: typeof BigInt === 'function' ? BigInt(bigint) : null,
};
}
}

View File

@@ -0,0 +1,108 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.parse = parse;
var _HermesParserDeserializer = _interopRequireDefault(require("./HermesParserDeserializer"));
var _HermesParserWASM = _interopRequireDefault(require("./HermesParserWASM"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
let HermesParserWASM;
let hermesParse;
let hermesParseResult_free;
let hermesParseResult_getError;
let hermesParseResult_getErrorLine;
let hermesParseResult_getErrorColumn;
let hermesParseResult_getProgramBuffer;
let hermesParseResult_getPositionBuffer;
let hermesParseResult_getPositionBufferSize;
/**
* Init the WASM wrapper code generated by `emscripten` to preparse the
* HermesParser WASM code.
*/
function initHermesParserWASM() {
if (HermesParserWASM != null) {
return;
}
HermesParserWASM = (0, _HermesParserWASM.default)({
/**
* The emscripten version of `quit` unconditionally assigns the `status` to
* `process.exitCode` which overrides any pre-existing code that has been
* set, even if it is non zero. For our use case we never want an
* `exitCode` to be set so this override removes that functionality.
*/
quit(_status, toThrow) {
throw toThrow;
}
});
hermesParse = HermesParserWASM.cwrap('hermesParse', 'number', ['number', 'number', 'number', 'number', 'number', 'number']);
hermesParseResult_free = HermesParserWASM.cwrap('hermesParseResult_free', 'void', ['number']);
hermesParseResult_getError = HermesParserWASM.cwrap('hermesParseResult_getError', 'string', ['number']);
hermesParseResult_getErrorLine = HermesParserWASM.cwrap('hermesParseResult_getErrorLine', 'number', ['number']);
hermesParseResult_getErrorColumn = HermesParserWASM.cwrap('hermesParseResult_getErrorColumn', 'number', ['number']);
hermesParseResult_getProgramBuffer = HermesParserWASM.cwrap('hermesParseResult_getProgramBuffer', 'number', ['number']);
hermesParseResult_getPositionBuffer = HermesParserWASM.cwrap('hermesParseResult_getPositionBuffer', 'number', ['number']);
hermesParseResult_getPositionBufferSize = HermesParserWASM.cwrap('hermesParseResult_getPositionBufferSize', 'number', ['number']);
} // Copy a string into the WASM heap and null-terminate
function copyToHeap(buffer, addr) {
HermesParserWASM.HEAP8.set(buffer, addr);
HermesParserWASM.HEAP8[addr + buffer.length] = 0;
}
function parse(source, options) {
initHermesParserWASM(); // Allocate space on heap for source text
const sourceBuffer = Buffer.from(source, 'utf8');
const sourceAddr = HermesParserWASM._malloc(sourceBuffer.length + 1);
if (!sourceAddr) {
throw new Error('Parser out of memory');
}
try {
// Copy source text onto WASM heap
copyToHeap(sourceBuffer, sourceAddr);
const parseResult = hermesParse(sourceAddr, sourceBuffer.length + 1, options.flow === 'detect', options.enableExperimentalComponentSyntax, options.tokens, options.allowReturnOutsideFunction);
try {
// Extract and throw error from parse result if parsing failed
const err = hermesParseResult_getError(parseResult);
if (err) {
const syntaxError = new SyntaxError(err); // $FlowExpectedError[prop-missing]
syntaxError.loc = {
line: hermesParseResult_getErrorLine(parseResult),
column: hermesParseResult_getErrorColumn(parseResult)
};
throw syntaxError;
}
const deserializer = new _HermesParserDeserializer.default(hermesParseResult_getProgramBuffer(parseResult), hermesParseResult_getPositionBuffer(parseResult), hermesParseResult_getPositionBufferSize(parseResult), HermesParserWASM, options);
return deserializer.deserialize();
} finally {
hermesParseResult_free(parseResult);
}
} finally {
HermesParserWASM._free(sourceAddr);
}
}

View File

@@ -0,0 +1,159 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
'use strict';
import type {HermesNode} from './HermesAST';
import type {ParserOptions} from './ParserOptions';
import HermesParserDeserializer from './HermesParserDeserializer';
import HermesParserWASMModule from './HermesParserWASM';
let HermesParserWASM;
let hermesParse;
let hermesParseResult_free;
let hermesParseResult_getError;
let hermesParseResult_getErrorLine;
let hermesParseResult_getErrorColumn;
let hermesParseResult_getProgramBuffer;
let hermesParseResult_getPositionBuffer;
let hermesParseResult_getPositionBufferSize;
/**
* Init the WASM wrapper code generated by `emscripten` to preparse the
* HermesParser WASM code.
*/
function initHermesParserWASM() {
if (HermesParserWASM != null) {
return;
}
HermesParserWASM = HermesParserWASMModule({
/**
* The emscripten version of `quit` unconditionally assigns the `status` to
* `process.exitCode` which overrides any pre-existing code that has been
* set, even if it is non zero. For our use case we never want an
* `exitCode` to be set so this override removes that functionality.
*/
quit(_status: number, toThrow: Error) {
throw toThrow;
},
});
hermesParse = HermesParserWASM.cwrap('hermesParse', 'number', [
'number',
'number',
'number',
'number',
'number',
'number',
]);
hermesParseResult_free = HermesParserWASM.cwrap(
'hermesParseResult_free',
'void',
['number'],
);
hermesParseResult_getError = HermesParserWASM.cwrap(
'hermesParseResult_getError',
'string',
['number'],
);
hermesParseResult_getErrorLine = HermesParserWASM.cwrap(
'hermesParseResult_getErrorLine',
'number',
['number'],
);
hermesParseResult_getErrorColumn = HermesParserWASM.cwrap(
'hermesParseResult_getErrorColumn',
'number',
['number'],
);
hermesParseResult_getProgramBuffer = HermesParserWASM.cwrap(
'hermesParseResult_getProgramBuffer',
'number',
['number'],
);
hermesParseResult_getPositionBuffer = HermesParserWASM.cwrap(
'hermesParseResult_getPositionBuffer',
'number',
['number'],
);
hermesParseResult_getPositionBufferSize = HermesParserWASM.cwrap(
'hermesParseResult_getPositionBufferSize',
'number',
['number'],
);
}
// Copy a string into the WASM heap and null-terminate
function copyToHeap(buffer: Buffer, addr: number) {
HermesParserWASM.HEAP8.set(buffer, addr);
HermesParserWASM.HEAP8[addr + buffer.length] = 0;
}
export function parse(source: string, options: ParserOptions): HermesNode {
initHermesParserWASM();
// Allocate space on heap for source text
const sourceBuffer = Buffer.from(source, 'utf8');
const sourceAddr = HermesParserWASM._malloc(sourceBuffer.length + 1);
if (!sourceAddr) {
throw new Error('Parser out of memory');
}
try {
// Copy source text onto WASM heap
copyToHeap(sourceBuffer, sourceAddr);
const parseResult = hermesParse(
sourceAddr,
sourceBuffer.length + 1,
options.flow === 'detect',
options.enableExperimentalComponentSyntax,
options.tokens,
options.allowReturnOutsideFunction,
);
try {
// Extract and throw error from parse result if parsing failed
const err = hermesParseResult_getError(parseResult);
if (err) {
const syntaxError = new SyntaxError(err);
// $FlowExpectedError[prop-missing]
syntaxError.loc = {
line: hermesParseResult_getErrorLine(parseResult),
column: hermesParseResult_getErrorColumn(parseResult),
};
throw syntaxError;
}
const deserializer = new HermesParserDeserializer(
hermesParseResult_getProgramBuffer(parseResult),
hermesParseResult_getPositionBuffer(parseResult),
hermesParseResult_getPositionBufferSize(parseResult),
HermesParserWASM,
options,
);
return deserializer.deserialize();
} finally {
hermesParseResult_free(parseResult);
}
} finally {
HermesParserWASM._free(sourceAddr);
}
}

View File

@@ -0,0 +1,68 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
'use strict';
/**
* Decode a UTF-8 encoded string from Hermes with a known length.
* Based on Emscripten's UTF8ToString with the following differences:
* - Always reads all bytes up to the given length, including null bytes. This
* means that we can decode strings that contain null bytes in the middle.
* - Allow UTF-8 encoded code points that are part of a surrogate pair, even though
* this is technically invalid UTF-8 that UTF8ToString would convert to 0xfffd.
*/
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = HermesParserDecodeUTF8String;
function HermesParserDecodeUTF8String(ptrIn, length, heap) {
let ptr = ptrIn;
const endPtr = ptr + length;
let str = '';
while (ptr < endPtr) {
// ASCII characters fit in single byte code point
let u0 = heap[ptr++];
if (!(u0 & 0x80)) {
str += String.fromCharCode(u0);
continue;
} // Two byte code point
const u1 = heap[ptr++] & 0x3f;
if ((u0 & 0xe0) === 0xc0) {
str += String.fromCharCode((u0 & 0x1f) << 6 | u1);
continue;
}
const u2 = heap[ptr++] & 0x3f;
if ((u0 & 0xf0) === 0xe0) {
// Three byte code point
u0 = (u0 & 0x0f) << 12 | u1 << 6 | u2;
} else {
// Four byte code point
u0 = (u0 & 0x07) << 18 | u1 << 12 | u2 << 6 | heap[ptr++] & 0x3f;
}
if (u0 < 0x10000) {
// Code point fits into a single UTF-16 code unit
str += String.fromCharCode(u0);
} else {
// Code point does not fit into single UTF-16 code unit so convert to surrogate pair
u0 -= 0x10000;
str += String.fromCharCode(0xd800 | u0 >> 10, 0xdc00 | u0 & 0x3ff);
}
}
return str;
}

View File

@@ -0,0 +1,65 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
'use strict';
/**
* Decode a UTF-8 encoded string from Hermes with a known length.
* Based on Emscripten's UTF8ToString with the following differences:
* - Always reads all bytes up to the given length, including null bytes. This
* means that we can decode strings that contain null bytes in the middle.
* - Allow UTF-8 encoded code points that are part of a surrogate pair, even though
* this is technically invalid UTF-8 that UTF8ToString would convert to 0xfffd.
*/
export default function HermesParserDecodeUTF8String(
ptrIn: number,
length: number,
heap: Uint8Array,
): string {
let ptr = ptrIn;
const endPtr = ptr + length;
let str = '';
while (ptr < endPtr) {
// ASCII characters fit in single byte code point
let u0 = heap[ptr++];
if (!(u0 & 0x80)) {
str += String.fromCharCode(u0);
continue;
}
// Two byte code point
const u1 = heap[ptr++] & 0x3f;
if ((u0 & 0xe0) === 0xc0) {
str += String.fromCharCode(((u0 & 0x1f) << 6) | u1);
continue;
}
const u2 = heap[ptr++] & 0x3f;
if ((u0 & 0xf0) === 0xe0) {
// Three byte code point
u0 = ((u0 & 0x0f) << 12) | (u1 << 6) | u2;
} else {
// Four byte code point
u0 = ((u0 & 0x07) << 18) | (u1 << 12) | (u2 << 6) | (heap[ptr++] & 0x3f);
}
if (u0 < 0x10000) {
// Code point fits into a single UTF-16 code unit
str += String.fromCharCode(u0);
} else {
// Code point does not fit into single UTF-16 code unit so convert to surrogate pair
u0 -= 0x10000;
str += String.fromCharCode(0xd800 | (u0 >> 10), 0xdc00 | (u0 & 0x3ff));
}
}
return str;
}

View File

@@ -0,0 +1,243 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _HermesParserDecodeUTF8String = _interopRequireDefault(require("./HermesParserDecodeUTF8String"));
var _HermesParserNodeDeserializers = _interopRequireDefault(require("./HermesParserNodeDeserializers"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
class HermesParserDeserializer {
// Matches StoredComment::Kind enum in JSLexer.h
// Matches TokenType enum in HermesParserJSSerializer.h
constructor(programBuffer, positionBuffer, positionBufferSize, wasmParser, options) {
this.programBufferIdx = void 0;
this.positionBufferIdx = void 0;
this.positionBufferSize = void 0;
this.locMap = void 0;
this.HEAPU8 = void 0;
this.HEAPU32 = void 0;
this.HEAPF64 = void 0;
this.options = void 0;
this.commentTypes = ['CommentLine', 'CommentBlock', 'InterpreterDirective'];
this.tokenTypes = ['Boolean', 'Identifier', 'Keyword', 'Null', 'Numeric', 'BigInt', 'Punctuator', 'String', 'RegularExpression', 'Template', 'JSXText'];
// Program and position buffer are memory addresses, so we must convert
// into indices into HEAPU32 (an array of 4-byte integers).
this.programBufferIdx = programBuffer / 4;
this.positionBufferIdx = positionBuffer / 4;
this.positionBufferSize = positionBufferSize;
this.locMap = {};
this.HEAPU8 = wasmParser.HEAPU8;
this.HEAPU32 = wasmParser.HEAPU32;
this.HEAPF64 = wasmParser.HEAPF64;
this.options = options;
}
/**
* Consume and return the next 4 bytes in the program buffer.
*/
next() {
const num = this.HEAPU32[this.programBufferIdx++];
return num;
}
deserialize() {
const program = {
type: 'Program',
loc: this.addEmptyLoc(),
body: this.deserializeNodeList(),
comments: this.deserializeComments()
};
if (this.options.tokens === true) {
program.tokens = this.deserializeTokens();
}
this.fillLocs();
return program;
}
/**
* Booleans are serialized as a single 4-byte integer.
*/
deserializeBoolean() {
return Boolean(this.next());
}
/**
* Numbers are serialized directly into program buffer, taking up 8 bytes
* preceded by 4 bytes of alignment padding if necessary.
*/
deserializeNumber() {
let floatIdx; // Numbers are aligned on 8-byte boundaries, so skip padding if we are at
// an odd index into the 4-byte aligned program buffer.
if (this.programBufferIdx % 2 === 0) {
floatIdx = this.programBufferIdx / 2;
this.programBufferIdx += 2;
} else {
floatIdx = (this.programBufferIdx + 1) / 2;
this.programBufferIdx += 3;
}
return this.HEAPF64[floatIdx];
}
/**
* Strings are serialized as a 4-byte pointer into the heap, followed
* by their size as a 4-byte integer. The size is only present if the
* pointer is non-null.
*/
deserializeString() {
const ptr = this.next();
if (ptr === 0) {
return null;
}
const size = this.next();
return (0, _HermesParserDecodeUTF8String.default)(ptr, size, this.HEAPU8);
}
/**
* Nodes are serialized as a 4-byte integer denoting their node kind,
* followed by a 4-byte loc ID, followed by serialized node properties.
*
* If the node kind is 0 the node is null, otherwise the node kind - 1 is an
* index into the array of node deserialization functions.
*/
deserializeNode() {
const nodeType = this.next();
if (nodeType === 0) {
return null;
}
const nodeDeserializer = _HermesParserNodeDeserializers.default[nodeType - 1].bind(this);
return nodeDeserializer();
}
/**
* Node lists are serialized as a 4-byte integer denoting the number of
* elements in the list, followed by the serialized elements.
*/
deserializeNodeList() {
const size = this.next();
const nodeList = [];
for (let i = 0; i < size; i++) {
nodeList.push(this.deserializeNode());
}
return nodeList;
}
/**
* Comments are serialized as a node list, where each comment is serialized
* as a 4-byte integer denoting comment type, followed by a 4-byte value
* denoting the loc ID, followed by a serialized string for the comment value.
*/
deserializeComments() {
const size = this.next();
const comments = [];
for (let i = 0; i < size; i++) {
const commentType = this.commentTypes[this.next()];
const loc = this.addEmptyLoc();
const value = this.deserializeString();
comments.push({
type: commentType,
loc,
value
});
}
return comments;
}
deserializeTokens() {
const size = this.next();
const tokens = [];
for (let i = 0; i < size; i++) {
const tokenType = this.tokenTypes[this.next()];
const loc = this.addEmptyLoc();
const value = this.deserializeString();
tokens.push({
type: tokenType,
loc,
value
});
}
return tokens;
}
/**
* While deserializing the AST locations are represented by
* a 4-byte loc ID. This is used to create a map of loc IDs to empty loc
* objects that are filled after the AST has been deserialized.
*/
addEmptyLoc() {
// $FlowExpectedError
const loc = {};
this.locMap[this.next()] = loc;
return loc;
}
/**
* Positions are serialized as a loc ID which denotes which loc it is associated with,
* followed by kind which denotes whether it is a start or end position,
* followed by line, column, and offset (4-bytes each).
*/
fillLocs() {
for (let i = 0; i < this.positionBufferSize; i++) {
const locId = this.HEAPU32[this.positionBufferIdx++];
const kind = this.HEAPU32[this.positionBufferIdx++];
const line = this.HEAPU32[this.positionBufferIdx++];
const column = this.HEAPU32[this.positionBufferIdx++];
const offset = this.HEAPU32[this.positionBufferIdx++];
const loc = this.locMap[locId];
if (kind === 0) {
loc.start = {
line,
column
};
loc.rangeStart = offset;
} else {
loc.end = {
line,
column
};
loc.rangeEnd = offset;
}
}
}
}
exports.default = HermesParserDeserializer;

View File

@@ -0,0 +1,261 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
'use strict';
import type {
HermesSourceLocation,
HermesNode,
HermesToken,
HermesComment,
} from './HermesAST';
import type {HermesParserWASM} from './HermesParserWASM';
import type {ParserOptions} from './ParserOptions';
import HermesParserDecodeUTF8String from './HermesParserDecodeUTF8String';
import NODE_DESERIALIZERS from './HermesParserNodeDeserializers';
export default class HermesParserDeserializer {
programBufferIdx: number;
positionBufferIdx: number;
+positionBufferSize: number;
+locMap: {[number]: HermesSourceLocation};
+HEAPU8: HermesParserWASM['HEAPU8'];
+HEAPU32: HermesParserWASM['HEAPU32'];
+HEAPF64: HermesParserWASM['HEAPF64'];
+options: ParserOptions;
// Matches StoredComment::Kind enum in JSLexer.h
+commentTypes: $ReadOnlyArray<HermesComment['type']> = [
'CommentLine',
'CommentBlock',
'InterpreterDirective',
];
// Matches TokenType enum in HermesParserJSSerializer.h
+tokenTypes: $ReadOnlyArray<HermesToken['type']> = [
'Boolean',
'Identifier',
'Keyword',
'Null',
'Numeric',
'BigInt',
'Punctuator',
'String',
'RegularExpression',
'Template',
'JSXText',
];
constructor(
programBuffer: number,
positionBuffer: number,
positionBufferSize: number,
wasmParser: HermesParserWASM,
options: ParserOptions,
) {
// Program and position buffer are memory addresses, so we must convert
// into indices into HEAPU32 (an array of 4-byte integers).
this.programBufferIdx = programBuffer / 4;
this.positionBufferIdx = positionBuffer / 4;
this.positionBufferSize = positionBufferSize;
this.locMap = {};
this.HEAPU8 = wasmParser.HEAPU8;
this.HEAPU32 = wasmParser.HEAPU32;
this.HEAPF64 = wasmParser.HEAPF64;
this.options = options;
}
/**
* Consume and return the next 4 bytes in the program buffer.
*/
next(): number {
const num = this.HEAPU32[this.programBufferIdx++];
return num;
}
deserialize(): HermesNode {
const program: HermesNode = {
type: 'Program',
loc: this.addEmptyLoc(),
body: this.deserializeNodeList(),
comments: this.deserializeComments(),
};
if (this.options.tokens === true) {
program.tokens = this.deserializeTokens();
}
this.fillLocs();
return program;
}
/**
* Booleans are serialized as a single 4-byte integer.
*/
deserializeBoolean(): boolean {
return Boolean(this.next());
}
/**
* Numbers are serialized directly into program buffer, taking up 8 bytes
* preceded by 4 bytes of alignment padding if necessary.
*/
deserializeNumber(): number {
let floatIdx;
// Numbers are aligned on 8-byte boundaries, so skip padding if we are at
// an odd index into the 4-byte aligned program buffer.
if (this.programBufferIdx % 2 === 0) {
floatIdx = this.programBufferIdx / 2;
this.programBufferIdx += 2;
} else {
floatIdx = (this.programBufferIdx + 1) / 2;
this.programBufferIdx += 3;
}
return this.HEAPF64[floatIdx];
}
/**
* Strings are serialized as a 4-byte pointer into the heap, followed
* by their size as a 4-byte integer. The size is only present if the
* pointer is non-null.
*/
deserializeString(): ?string {
const ptr = this.next();
if (ptr === 0) {
return null;
}
const size = this.next();
return HermesParserDecodeUTF8String(ptr, size, this.HEAPU8);
}
/**
* Nodes are serialized as a 4-byte integer denoting their node kind,
* followed by a 4-byte loc ID, followed by serialized node properties.
*
* If the node kind is 0 the node is null, otherwise the node kind - 1 is an
* index into the array of node deserialization functions.
*/
deserializeNode(): ?HermesNode {
const nodeType = this.next();
if (nodeType === 0) {
return null;
}
const nodeDeserializer = NODE_DESERIALIZERS[nodeType - 1].bind(this);
return nodeDeserializer();
}
/**
* Node lists are serialized as a 4-byte integer denoting the number of
* elements in the list, followed by the serialized elements.
*/
deserializeNodeList(): Array<?HermesNode> {
const size = this.next();
const nodeList = [];
for (let i = 0; i < size; i++) {
nodeList.push(this.deserializeNode());
}
return nodeList;
}
/**
* Comments are serialized as a node list, where each comment is serialized
* as a 4-byte integer denoting comment type, followed by a 4-byte value
* denoting the loc ID, followed by a serialized string for the comment value.
*/
deserializeComments(): Array<HermesComment> {
const size = this.next();
const comments = [];
for (let i = 0; i < size; i++) {
const commentType = this.commentTypes[this.next()];
const loc = this.addEmptyLoc();
const value = this.deserializeString();
comments.push({
type: commentType,
loc,
value,
});
}
return comments;
}
deserializeTokens(): Array<HermesToken> {
const size = this.next();
const tokens = [];
for (let i = 0; i < size; i++) {
const tokenType = this.tokenTypes[this.next()];
const loc = this.addEmptyLoc();
const value = this.deserializeString();
tokens.push({
type: tokenType,
loc,
value,
});
}
return tokens;
}
/**
* While deserializing the AST locations are represented by
* a 4-byte loc ID. This is used to create a map of loc IDs to empty loc
* objects that are filled after the AST has been deserialized.
*/
addEmptyLoc(): HermesSourceLocation {
// $FlowExpectedError
const loc: HermesSourceLocation = {};
this.locMap[this.next()] = loc;
return loc;
}
/**
* Positions are serialized as a loc ID which denotes which loc it is associated with,
* followed by kind which denotes whether it is a start or end position,
* followed by line, column, and offset (4-bytes each).
*/
fillLocs(): void {
for (let i = 0; i < this.positionBufferSize; i++) {
const locId = this.HEAPU32[this.positionBufferIdx++];
const kind = this.HEAPU32[this.positionBufferIdx++];
const line = this.HEAPU32[this.positionBufferIdx++];
const column = this.HEAPU32[this.positionBufferIdx++];
const offset = this.HEAPU32[this.positionBufferIdx++];
const loc = this.locMap[locId];
if (kind === 0) {
loc.start = {
line,
column,
};
loc.rangeStart = offset;
} else {
loc.end = {
line,
column,
};
loc.rangeEnd = offset;
}
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,16 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {HermesNode} from './HermesAST';
import type HermesParserDeserializer from './HermesParserDeserializer';
declare module.exports: $ReadOnlyArray<
(this: HermesParserDeserializer) => HermesNode,
>;

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,79 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
type INTENTIONAL_ANY = $FlowFixMe;
type JSType = 'number' | 'string' | 'array' | 'boolean';
type CBoolean = ?(number | boolean);
type WASMModuleOverrides = $ReadOnly<{
quit(status: number, toThrow: Error): void,
}>;
export type HermesParserWASM = $ReadOnly<{
HEAP8: Int8Array,
HEAP16: Int16Array,
HEAP32: Int32Array,
HEAPU8: Uint8Array,
HEAPU16: Uint16Array,
HEAPU32: Uint32Array,
HEAPF32: Float32Array,
HEAPF64: Float64Array,
_malloc(size: number): number,
_free(ptr: number): void,
ccall(
ident: string,
returnType: JSType | null,
argTypes: $ReadOnlyArray<JSType>,
args: $ReadOnlyArray<
number | string | $ReadOnlyArray<INTENTIONAL_ANY> | boolean,
>,
opts?: $ReadOnly<{
async?: boolean | void,
}>,
): INTENTIONAL_ANY,
cwrap: {
(
'hermesParse',
'number',
['number', 'number', 'number', 'number', 'number', 'number'],
): (number, number, CBoolean, CBoolean, CBoolean, CBoolean) => number,
('hermesParseResult_free', 'void', ['number']): number => void,
('hermesParseResult_getError', 'string', ['number']): number => string,
('hermesParseResult_getErrorLine', 'number', ['number']): number => number,
(
'hermesParseResult_getErrorColumn',
'number',
['number'],
): number => number,
(
'hermesParseResult_getProgramBuffer',
'number',
['number'],
): number => number,
(
'hermesParseResult_getPositionBuffer',
'number',
['number'],
): number => number,
(
'hermesParseResult_getPositionBufferSize',
'number',
['number'],
): number => number,
},
stackAlloc(size: number): number,
stackSave(): number,
stackRestore(ptr: number): void,
}>;
declare module.exports: WASMModuleOverrides => HermesParserWASM;

View File

@@ -0,0 +1,441 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
var _HermesASTAdapter = _interopRequireDefault(require("./HermesASTAdapter"));
var _getModuleDocblock = require("./getModuleDocblock");
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
class HermesToESTreeAdapter extends _HermesASTAdapter.default {
constructor(options, code) {
super(options);
this.code = void 0;
this.code = code;
}
fixSourceLocation(node) {
var _this$sourceFilename;
const loc = node.loc;
if (loc == null) {
return;
}
node.loc = {
source: (_this$sourceFilename = this.sourceFilename) != null ? _this$sourceFilename : null,
start: loc.start,
end: loc.end
};
node.range = [loc.rangeStart, loc.rangeEnd];
delete node.start;
delete node.end;
}
mapNode(node) {
this.fixSourceLocation(node);
switch (node.type) {
case 'Program':
return this.mapProgram(node);
case 'NullLiteral':
return this.mapNullLiteral(node);
case 'BooleanLiteral':
case 'StringLiteral':
case 'NumericLiteral':
case 'JSXStringLiteral':
return this.mapSimpleLiteral(node);
case 'BigIntLiteral':
return this.mapBigIntLiteral(node);
case 'RegExpLiteral':
return this.mapRegExpLiteral(node);
case 'Empty':
return this.mapEmpty(node);
case 'TemplateElement':
return this.mapTemplateElement(node);
case 'BigIntLiteralTypeAnnotation':
return this.mapBigIntLiteralTypeAnnotation(node);
case 'GenericTypeAnnotation':
return this.mapGenericTypeAnnotation(node);
case 'ImportDeclaration':
return this.mapImportDeclaration(node);
case 'ImportSpecifier':
return this.mapImportSpecifier(node);
case 'ExportDefaultDeclaration':
return this.mapExportDefaultDeclaration(node);
case 'ExportNamedDeclaration':
return this.mapExportNamedDeclaration(node);
case 'ExportAllDeclaration':
return this.mapExportAllDeclaration(node);
case 'Property':
return this.mapProperty(node);
case 'FunctionDeclaration':
case 'FunctionExpression':
case 'ArrowFunctionExpression':
return this.mapFunction(node);
case 'PrivateName':
return this.mapPrivateName(node);
case 'ClassProperty':
case 'ClassPrivateProperty':
return this.mapClassProperty(node);
case 'MemberExpression':
case 'OptionalMemberExpression':
case 'CallExpression':
case 'OptionalCallExpression':
return this.mapChainExpression(node);
default:
return this.mapNodeDefault(node);
}
}
mapProgram(node) {
const nodeDefault = this.mapNodeDefault(node);
node.sourceType = this.getSourceType();
node.docblock = (0, _getModuleDocblock.getModuleDocblock)(nodeDefault);
return nodeDefault;
}
mapSimpleLiteral(node) {
return {
type: 'Literal',
loc: node.loc,
range: node.range,
value: node.value,
raw: this.code.slice(node.range[0], node.range[1]),
literalType: (() => {
switch (node.type) {
case 'NullLiteral':
return 'null';
case 'BooleanLiteral':
return 'boolean';
case 'StringLiteral':
case 'JSXStringLiteral':
return 'string';
case 'NumericLiteral':
return 'numeric';
case 'BigIntLiteral':
return 'bigint';
case 'RegExpLiteral':
return 'regexp';
}
return null;
})()
};
}
mapBigIntLiteral(node) {
const newNode = this.mapSimpleLiteral(node);
return { ...newNode,
...this.getBigIntLiteralValue(node.bigint)
};
}
mapNullLiteral(node) {
return { ...this.mapSimpleLiteral(node),
value: null
};
}
mapRegExpLiteral(node) {
const {
pattern,
flags
} = node; // Create RegExp value if possible. This can fail when the flags are invalid.
let value;
try {
value = new RegExp(pattern, flags);
} catch (e) {
value = null;
}
return { ...this.mapSimpleLiteral(node),
value,
regex: {
pattern,
flags
}
};
}
mapBigIntLiteralTypeAnnotation(node) {
return { ...node,
...this.getBigIntLiteralValue(node.raw)
};
}
mapTemplateElement(node) {
return {
type: 'TemplateElement',
loc: node.loc,
range: node.range,
tail: node.tail,
value: {
cooked: node.cooked,
raw: node.raw
}
};
}
mapGenericTypeAnnotation(node) {
// Convert simple `this` generic type to ThisTypeAnnotation
if (node.typeParameters == null && node.id.type === 'Identifier' && node.id.name === 'this') {
return {
type: 'ThisTypeAnnotation',
loc: node.loc,
range: node.range
};
}
return this.mapNodeDefault(node);
}
mapProperty(nodeUnprocessed) {
const node = this.mapNodeDefault(nodeUnprocessed);
if (node.value.type === 'FunctionExpression' && (node.method || node.kind !== 'init')) {
node.value.loc.start = node.key.loc.end;
node.value.range[0] = node.key.range[1];
}
return node;
}
mapComment(node) {
if (node.type === 'CommentBlock') {
node.type = 'Block';
} else if (node.type === 'CommentLine') {
node.type = 'Line';
}
return node;
}
mapFunction(nodeUnprocessed) {
const node = this.mapNodeDefault(nodeUnprocessed);
switch (node.type) {
// This prop should ideally only live on `ArrowFunctionExpression` but to
// match espree output we place it on all functions types.
case 'FunctionDeclaration':
case 'FunctionExpression':
node.expression = false;
return node;
case 'ArrowFunctionExpression':
node.expression = node.body.type !== 'BlockStatement';
return node;
}
return node;
}
mapChainExpression(nodeUnprocessed) {
/*
NOTE - In the below comments `MemberExpression` and `CallExpression`
are completely interchangable. For terseness we just reference
one each time.
*/
/*
Hermes uses the old babel-style AST:
```
(one?.two).three?.four;
^^^^^^^^^^^^^^^^^^^^^^ OptionalMemberExpression
^^^^^^^^^^^^^^^^ MemberExpression
^^^^^^^^ OptionalMemberExpression
```
We need to convert it to the ESTree representation:
```
(one?.two).three?.four;
^^^^^^^^^^^^^^^^^^^^^^ ChainExpression
^^^^^^^^^^^^^^^^^^^^^^ MemberExpression[optional = true]
^^^^^^^^^^^^^^^^ MemberExpression[optional = false]
^^^^^^^^ ChainExpression
^^^^^^^^ MemberExpression[optional = true]
```
We do this by converting the AST and its children (depth first), and then unwrapping
the resulting AST as appropriate.
Put another way:
1) traverse to the leaf
2) if the current node is an `OptionalMemberExpression`:
a) if the `.object` is a `ChainExpression`:
i) unwrap the child (`node.object = child.expression`)
b) convert this node to a `MemberExpression[optional = true]`
c) wrap this node (`node = ChainExpression[expression = node]`)
3) if the current node is a `MemberExpression`:
a) convert this node to a `MemberExpression[optional = true]`
*/
const node = this.mapNodeDefault(nodeUnprocessed);
const {
child,
childKey,
isOptional
} = (() => {
const isOptional = node.optional === true;
if (node.type.endsWith('MemberExpression')) {
return {
child: node.object,
childKey: 'object',
isOptional
};
} else if (node.type.endsWith('CallExpression')) {
return {
child: node.callee,
childKey: 'callee',
isOptional
};
} else {
return {
child: node.expression,
childKey: 'expression',
isOptional: false
};
}
})();
const isChildUnwrappable = child.type === 'ChainExpression' && // (x?.y).z is semantically different to `x?.y.z`.
// In the un-parenthesised case `.z` is only executed if and only if `x?.y` returns a non-nullish value.
// In the parenthesised case, `.z` is **always** executed, regardless of the return of `x?.y`.
// As such the AST is different between the two cases.
//
// In the hermes AST - any member part of a non-short-circuited optional chain is represented with `OptionalMemberExpression`
// so if we see a `MemberExpression`, then we know we've hit a parenthesis boundary.
node.type !== 'MemberExpression' && node.type !== 'CallExpression';
if (node.type.startsWith('Optional')) {
node.type = node.type.replace('Optional', '');
node.optional = isOptional;
} else {
node.optional = false;
}
if (!isChildUnwrappable && !isOptional) {
return node;
}
if (isChildUnwrappable) {
const newChild = child.expression;
node[childKey] = newChild;
}
return {
type: 'ChainExpression',
expression: node,
loc: node.loc,
range: node.range
};
}
mapClassProperty(nodeUnprocessed) {
const node = this.mapNodeDefault(nodeUnprocessed);
const key = (() => {
if (node.type === 'ClassPrivateProperty') {
const key = this.mapNodeDefault(node.key);
return {
type: 'PrivateIdentifier',
name: key.name,
range: key.range,
loc: key.loc
};
}
return node.key;
})();
return { ...node,
computed: node.type === 'ClassPrivateProperty' ? false : node.computed,
key,
type: 'PropertyDefinition'
};
}
mapPrivateName(node) {
return {
type: 'PrivateIdentifier',
name: node.id.name,
// estree the location refers to the entire string including the hash token
range: node.range,
loc: node.loc
};
}
mapExportNamedDeclaration(nodeUnprocessed) {
const node = super.mapExportNamedDeclaration(nodeUnprocessed);
const namespaceSpecifier = node.specifiers.find(spec => spec.type === 'ExportNamespaceSpecifier');
if (namespaceSpecifier != null) {
var _node$exportKind;
if (node.specifiers.length !== 1) {
// this should already a hermes parser error - but let's be absolutely sure we're aligned with the spec
throw new Error('Cannot use an export all with any other specifiers');
}
return {
type: 'ExportAllDeclaration',
source: node.source,
exportKind: (_node$exportKind = node.exportKind) != null ? _node$exportKind : 'value',
exported: namespaceSpecifier.exported,
range: node.range,
loc: node.loc
};
}
return node;
}
mapExportAllDeclaration(nodeUnprocessed) {
var _node$exported;
const node = super.mapExportAllDeclaration(nodeUnprocessed);
node.exported = (_node$exported = node.exported) != null ? _node$exported : null;
return node;
}
}
exports.default = HermesToESTreeAdapter;

View File

@@ -0,0 +1,427 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
import type {HermesNode} from './HermesAST';
import type {ParserOptions} from './ParserOptions';
import HermesASTAdapter from './HermesASTAdapter';
import {getModuleDocblock} from './getModuleDocblock';
declare var BigInt: ?(value: $FlowFixMe) => mixed;
export default class HermesToESTreeAdapter extends HermesASTAdapter {
+code: string;
constructor(options: ParserOptions, code: string) {
super(options);
this.code = code;
}
fixSourceLocation(node: HermesNode): void {
const loc = node.loc;
if (loc == null) {
return;
}
node.loc = {
source: this.sourceFilename ?? null,
start: loc.start,
end: loc.end,
};
node.range = [loc.rangeStart, loc.rangeEnd];
delete node.start;
delete node.end;
}
mapNode(node: HermesNode): HermesNode {
this.fixSourceLocation(node);
switch (node.type) {
case 'Program':
return this.mapProgram(node);
case 'NullLiteral':
return this.mapNullLiteral(node);
case 'BooleanLiteral':
case 'StringLiteral':
case 'NumericLiteral':
case 'JSXStringLiteral':
return this.mapSimpleLiteral(node);
case 'BigIntLiteral':
return this.mapBigIntLiteral(node);
case 'RegExpLiteral':
return this.mapRegExpLiteral(node);
case 'Empty':
return this.mapEmpty(node);
case 'TemplateElement':
return this.mapTemplateElement(node);
case 'BigIntLiteralTypeAnnotation':
return this.mapBigIntLiteralTypeAnnotation(node);
case 'GenericTypeAnnotation':
return this.mapGenericTypeAnnotation(node);
case 'ImportDeclaration':
return this.mapImportDeclaration(node);
case 'ImportSpecifier':
return this.mapImportSpecifier(node);
case 'ExportDefaultDeclaration':
return this.mapExportDefaultDeclaration(node);
case 'ExportNamedDeclaration':
return this.mapExportNamedDeclaration(node);
case 'ExportAllDeclaration':
return this.mapExportAllDeclaration(node);
case 'Property':
return this.mapProperty(node);
case 'FunctionDeclaration':
case 'FunctionExpression':
case 'ArrowFunctionExpression':
return this.mapFunction(node);
case 'PrivateName':
return this.mapPrivateName(node);
case 'ClassProperty':
case 'ClassPrivateProperty':
return this.mapClassProperty(node);
case 'MemberExpression':
case 'OptionalMemberExpression':
case 'CallExpression':
case 'OptionalCallExpression':
return this.mapChainExpression(node);
default:
return this.mapNodeDefault(node);
}
}
mapProgram(node: HermesNode): HermesNode {
const nodeDefault = this.mapNodeDefault(node);
node.sourceType = this.getSourceType();
node.docblock = getModuleDocblock(nodeDefault);
return nodeDefault;
}
mapSimpleLiteral(node: HermesNode): HermesNode {
return {
type: 'Literal',
loc: node.loc,
range: node.range,
value: node.value,
raw: this.code.slice(node.range[0], node.range[1]),
literalType: (() => {
switch (node.type) {
case 'NullLiteral':
return 'null';
case 'BooleanLiteral':
return 'boolean';
case 'StringLiteral':
case 'JSXStringLiteral':
return 'string';
case 'NumericLiteral':
return 'numeric';
case 'BigIntLiteral':
return 'bigint';
case 'RegExpLiteral':
return 'regexp';
}
return null;
})(),
};
}
mapBigIntLiteral(node: HermesNode): HermesNode {
const newNode = this.mapSimpleLiteral(node);
return {
...newNode,
...this.getBigIntLiteralValue(node.bigint),
};
}
mapNullLiteral(node: HermesNode): HermesNode {
return {
...this.mapSimpleLiteral(node),
value: null,
};
}
mapRegExpLiteral(node: HermesNode): HermesNode {
const {pattern, flags} = node;
// Create RegExp value if possible. This can fail when the flags are invalid.
let value;
try {
value = new RegExp(pattern, flags);
} catch (e) {
value = null;
}
return {
...this.mapSimpleLiteral(node),
value,
regex: {
pattern,
flags,
},
};
}
mapBigIntLiteralTypeAnnotation(node: HermesNode): HermesNode {
return {
...node,
...this.getBigIntLiteralValue(node.raw),
};
}
mapTemplateElement(node: HermesNode): HermesNode {
return {
type: 'TemplateElement',
loc: node.loc,
range: node.range,
tail: node.tail,
value: {
cooked: node.cooked,
raw: node.raw,
},
};
}
mapGenericTypeAnnotation(node: HermesNode): HermesNode {
// Convert simple `this` generic type to ThisTypeAnnotation
if (
node.typeParameters == null &&
node.id.type === 'Identifier' &&
node.id.name === 'this'
) {
return {
type: 'ThisTypeAnnotation',
loc: node.loc,
range: node.range,
};
}
return this.mapNodeDefault(node);
}
mapProperty(nodeUnprocessed: HermesNode): HermesNode {
const node = this.mapNodeDefault(nodeUnprocessed);
if (
node.value.type === 'FunctionExpression' &&
(node.method || node.kind !== 'init')
) {
node.value.loc.start = node.key.loc.end;
node.value.range[0] = node.key.range[1];
}
return node;
}
mapComment(node: HermesNode): HermesNode {
if (node.type === 'CommentBlock') {
node.type = 'Block';
} else if (node.type === 'CommentLine') {
node.type = 'Line';
}
return node;
}
mapFunction(nodeUnprocessed: HermesNode): HermesNode {
const node = this.mapNodeDefault(nodeUnprocessed);
switch (node.type) {
// This prop should ideally only live on `ArrowFunctionExpression` but to
// match espree output we place it on all functions types.
case 'FunctionDeclaration':
case 'FunctionExpression':
node.expression = false;
return node;
case 'ArrowFunctionExpression':
node.expression = node.body.type !== 'BlockStatement';
return node;
}
return node;
}
mapChainExpression(nodeUnprocessed: HermesNode): HermesNode {
/*
NOTE - In the below comments `MemberExpression` and `CallExpression`
are completely interchangable. For terseness we just reference
one each time.
*/
/*
Hermes uses the old babel-style AST:
```
(one?.two).three?.four;
^^^^^^^^^^^^^^^^^^^^^^ OptionalMemberExpression
^^^^^^^^^^^^^^^^ MemberExpression
^^^^^^^^ OptionalMemberExpression
```
We need to convert it to the ESTree representation:
```
(one?.two).three?.four;
^^^^^^^^^^^^^^^^^^^^^^ ChainExpression
^^^^^^^^^^^^^^^^^^^^^^ MemberExpression[optional = true]
^^^^^^^^^^^^^^^^ MemberExpression[optional = false]
^^^^^^^^ ChainExpression
^^^^^^^^ MemberExpression[optional = true]
```
We do this by converting the AST and its children (depth first), and then unwrapping
the resulting AST as appropriate.
Put another way:
1) traverse to the leaf
2) if the current node is an `OptionalMemberExpression`:
a) if the `.object` is a `ChainExpression`:
i) unwrap the child (`node.object = child.expression`)
b) convert this node to a `MemberExpression[optional = true]`
c) wrap this node (`node = ChainExpression[expression = node]`)
3) if the current node is a `MemberExpression`:
a) convert this node to a `MemberExpression[optional = true]`
*/
const node = this.mapNodeDefault(nodeUnprocessed);
const {child, childKey, isOptional} = ((): {
child: HermesNode,
childKey: string,
isOptional: boolean,
} => {
const isOptional: boolean = node.optional === true;
if (node.type.endsWith('MemberExpression')) {
return {
child: node.object,
childKey: 'object',
isOptional,
};
} else if (node.type.endsWith('CallExpression')) {
return {
child: node.callee,
childKey: 'callee',
isOptional,
};
} else {
return {
child: node.expression,
childKey: 'expression',
isOptional: false,
};
}
})();
const isChildUnwrappable =
child.type === 'ChainExpression' &&
// (x?.y).z is semantically different to `x?.y.z`.
// In the un-parenthesised case `.z` is only executed if and only if `x?.y` returns a non-nullish value.
// In the parenthesised case, `.z` is **always** executed, regardless of the return of `x?.y`.
// As such the AST is different between the two cases.
//
// In the hermes AST - any member part of a non-short-circuited optional chain is represented with `OptionalMemberExpression`
// so if we see a `MemberExpression`, then we know we've hit a parenthesis boundary.
node.type !== 'MemberExpression' &&
node.type !== 'CallExpression';
if (node.type.startsWith('Optional')) {
node.type = node.type.replace('Optional', '');
node.optional = isOptional;
} else {
node.optional = false;
}
if (!isChildUnwrappable && !isOptional) {
return node;
}
if (isChildUnwrappable) {
const newChild = child.expression;
node[childKey] = newChild;
}
return {
type: 'ChainExpression',
expression: node,
loc: node.loc,
range: node.range,
};
}
mapClassProperty(nodeUnprocessed: HermesNode): HermesNode {
const node = this.mapNodeDefault(nodeUnprocessed);
const key = (() => {
if (node.type === 'ClassPrivateProperty') {
const key = this.mapNodeDefault(node.key);
return {
type: 'PrivateIdentifier',
name: key.name,
range: key.range,
loc: key.loc,
};
}
return node.key;
})();
return {
...node,
computed: node.type === 'ClassPrivateProperty' ? false : node.computed,
key,
type: 'PropertyDefinition',
};
}
mapPrivateName(node: HermesNode): HermesNode {
return {
type: 'PrivateIdentifier',
name: node.id.name,
// estree the location refers to the entire string including the hash token
range: node.range,
loc: node.loc,
};
}
mapExportNamedDeclaration(nodeUnprocessed: HermesNode): HermesNode {
const node = super.mapExportNamedDeclaration(nodeUnprocessed);
const namespaceSpecifier = node.specifiers.find(
spec => spec.type === 'ExportNamespaceSpecifier',
);
if (namespaceSpecifier != null) {
if (node.specifiers.length !== 1) {
// this should already a hermes parser error - but let's be absolutely sure we're aligned with the spec
throw new Error('Cannot use an export all with any other specifiers');
}
return {
type: 'ExportAllDeclaration',
source: node.source,
exportKind: node.exportKind ?? 'value',
exported: namespaceSpecifier.exported,
range: node.range,
loc: node.loc,
};
}
return node;
}
mapExportAllDeclaration(nodeUnprocessed: HermesNode): HermesNode {
const node = super.mapExportAllDeclaration(nodeUnprocessed);
node.exported = node.exported ?? null;
return node;
}
}

View File

@@ -0,0 +1,18 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.ParserOptionsKeys = void 0;
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
const ParserOptionsKeys = new Set(['allowReturnOutsideFunction', 'babel', 'flow', 'enableExperimentalComponentSyntax', 'reactRuntimeTarget', 'sourceFilename', 'sourceType', 'tokens']);
exports.ParserOptionsKeys = ParserOptionsKeys;

View File

@@ -0,0 +1,31 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
export type ParserOptions = {
allowReturnOutsideFunction?: boolean,
babel?: boolean,
flow?: 'all' | 'detect',
enableExperimentalComponentSyntax?: boolean,
reactRuntimeTarget?: '18' | '19',
sourceFilename?: string,
sourceType?: 'module' | 'script' | 'unambiguous',
tokens?: boolean,
};
export const ParserOptionsKeys: $ReadOnlySet<$Keys<ParserOptions>> = new Set([
'allowReturnOutsideFunction',
'babel',
'flow',
'enableExperimentalComponentSyntax',
'reactRuntimeTarget',
'sourceFilename',
'sourceType',
'tokens',
]);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,788 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
/**
* This transforms component syntax (https://flow.org/en/docs/react/component-syntax/)
* and hook syntax (https://flow.org/en/docs/react/hook-syntax/).
*
* It is expected that all transforms create valid ESTree AST output. If
* the transform requires outputting Babel specific AST nodes then it
* should live in `ConvertESTreeToBabel.js`
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.transformProgram = transformProgram;
var _SimpleTransform = require("../transform/SimpleTransform");
var _astNodeMutationHelpers = require("../transform/astNodeMutationHelpers");
var _SimpleTraverser = require("../traverse/SimpleTraverser");
var _createSyntaxError = require("../utils/createSyntaxError");
const nodeWith = _SimpleTransform.SimpleTransform.nodeWith; // Rely on the mapper to fix up parent relationships.
const EMPTY_PARENT = null;
function createDefaultPosition() {
return {
line: 1,
column: 0
};
}
function mapDeclareComponent(node) {
return {
type: 'DeclareVariable',
id: nodeWith(node.id, {
typeAnnotation: {
type: 'TypeAnnotation',
typeAnnotation: {
type: 'AnyTypeAnnotation',
loc: node.loc,
range: node.range,
parent: EMPTY_PARENT
},
loc: node.loc,
range: node.range,
parent: EMPTY_PARENT
}
}),
kind: 'const',
loc: node.loc,
range: node.range,
parent: node.parent
};
}
function getComponentParameterName(paramName) {
switch (paramName.type) {
case 'Identifier':
return paramName.name;
case 'Literal':
return paramName.value;
default:
throw (0, _createSyntaxError.createSyntaxError)(paramName, `Unknown Component parameter name type of "${paramName.type}"`);
}
}
function createPropsTypeAnnotation(propTypes, spread, loc, range) {
// Create empty loc for type annotation nodes
const createParamsTypeLoc = () => ({
loc: {
start: (loc == null ? void 0 : loc.start) != null ? loc.start : createDefaultPosition(),
end: (loc == null ? void 0 : loc.end) != null ? loc.end : createDefaultPosition()
},
range: range != null ? range : [0, 0],
parent: EMPTY_PARENT
}); // Optimize `{...Props}` -> `Props`
if (spread != null && propTypes.length === 0) {
return {
type: 'TypeAnnotation',
typeAnnotation: spread.argument,
...createParamsTypeLoc()
};
}
const typeProperties = [...propTypes];
if (spread != null) {
// Spread needs to be the first type, as inline properties take precedence.
typeProperties.unshift(spread);
}
const propTypeObj = {
type: 'ObjectTypeAnnotation',
callProperties: [],
properties: typeProperties,
indexers: [],
internalSlots: [],
exact: false,
inexact: false,
...createParamsTypeLoc()
};
return {
type: 'TypeAnnotation',
typeAnnotation: {
type: 'GenericTypeAnnotation',
id: {
type: 'Identifier',
name: '$ReadOnly',
optional: false,
typeAnnotation: null,
...createParamsTypeLoc()
},
typeParameters: {
type: 'TypeParameterInstantiation',
params: [propTypeObj],
...createParamsTypeLoc()
},
...createParamsTypeLoc()
},
...createParamsTypeLoc()
};
}
function mapComponentParameters(params, options) {
var _options$reactRuntime;
if (params.length === 0) {
return {
props: null,
ref: null
};
} // Optimize `component Foo(...props: Props) {}` to `function Foo(props: Props) {}
if (params.length === 1 && params[0].type === 'RestElement' && params[0].argument.type === 'Identifier') {
const restElementArgument = params[0].argument;
return {
props: restElementArgument,
ref: null
};
} // Filter out any ref param and capture it's details when targeting React 18.
// React 19+ treats ref as a regular prop for function components.
let refParam = null;
const paramsWithoutRef = ((_options$reactRuntime = options.reactRuntimeTarget) != null ? _options$reactRuntime : '18') === '18' ? params.filter(param => {
if (param.type === 'ComponentParameter' && getComponentParameterName(param.name) === 'ref') {
refParam = param;
return false;
}
return true;
}) : params;
const [propTypes, spread] = paramsWithoutRef.reduce(([propTypes, spread], param) => {
switch (param.type) {
case 'RestElement':
{
if (spread != null) {
throw (0, _createSyntaxError.createSyntaxError)(param, `Invalid state, multiple rest elements found as Component Parameters`);
}
return [propTypes, mapComponentParameterRestElementType(param)];
}
case 'ComponentParameter':
{
propTypes.push(mapComponentParameterType(param));
return [propTypes, spread];
}
}
}, [[], null]);
const propsProperties = paramsWithoutRef.flatMap(mapComponentParameter);
let props = null;
if (propsProperties.length === 0) {
if (refParam == null) {
throw new Error('StripComponentSyntax: Invalid state, ref should always be set at this point if props are empty');
}
const emptyParamsLoc = {
start: refParam.loc.start,
end: refParam.loc.start
};
const emptyParamsRange = [refParam.range[0], refParam.range[0]]; // no properties provided (must have had a single ref)
props = {
type: 'Identifier',
name: '_$$empty_props_placeholder$$',
optional: false,
typeAnnotation: createPropsTypeAnnotation([], null, emptyParamsLoc, emptyParamsRange),
loc: emptyParamsLoc,
range: emptyParamsRange,
parent: EMPTY_PARENT
};
} else {
const lastPropsProperty = propsProperties[propsProperties.length - 1];
props = {
type: 'ObjectPattern',
properties: propsProperties,
typeAnnotation: createPropsTypeAnnotation(propTypes, spread, {
start: lastPropsProperty.loc.end,
end: lastPropsProperty.loc.end
}, [lastPropsProperty.range[1], lastPropsProperty.range[1]]),
loc: {
start: propsProperties[0].loc.start,
end: lastPropsProperty.loc.end
},
range: [propsProperties[0].range[0], lastPropsProperty.range[1]],
parent: EMPTY_PARENT
};
}
let ref = null;
if (refParam != null) {
ref = refParam.local;
}
return {
props,
ref
};
}
function mapComponentParameterType(param) {
var _typeAnnotation$typeA;
const typeAnnotation = param.local.type === 'AssignmentPattern' ? param.local.left.typeAnnotation : param.local.typeAnnotation;
const optional = param.local.type === 'AssignmentPattern' ? true : param.local.type === 'Identifier' ? param.local.optional : false;
return {
type: 'ObjectTypeProperty',
key: (0, _astNodeMutationHelpers.shallowCloneNode)(param.name),
value: (_typeAnnotation$typeA = typeAnnotation == null ? void 0 : typeAnnotation.typeAnnotation) != null ? _typeAnnotation$typeA : {
type: 'AnyTypeAnnotation',
loc: param.local.loc,
range: param.local.range,
parent: EMPTY_PARENT
},
kind: 'init',
optional,
method: false,
static: false,
proto: false,
variance: null,
loc: param.local.loc,
range: param.local.range,
parent: EMPTY_PARENT
};
}
function mapComponentParameterRestElementType(param) {
var _param$argument$typeA, _param$argument$typeA2;
if (param.argument.type !== 'Identifier' && param.argument.type !== 'ObjectPattern') {
throw (0, _createSyntaxError.createSyntaxError)(param, `Invalid ${param.argument.type} encountered in restParameter`);
}
return {
type: 'ObjectTypeSpreadProperty',
argument: (_param$argument$typeA = (_param$argument$typeA2 = param.argument.typeAnnotation) == null ? void 0 : _param$argument$typeA2.typeAnnotation) != null ? _param$argument$typeA : {
type: 'AnyTypeAnnotation',
loc: param.loc,
range: param.range,
parent: EMPTY_PARENT
},
loc: param.loc,
range: param.range,
parent: EMPTY_PARENT
};
}
function mapComponentParameter(param) {
switch (param.type) {
case 'RestElement':
{
switch (param.argument.type) {
case 'Identifier':
{
const a = nodeWith(param, {
typeAnnotation: null,
argument: nodeWith(param.argument, {
typeAnnotation: null
})
});
return [a];
}
case 'ObjectPattern':
{
return param.argument.properties.map(property => {
return nodeWith(property, {
typeAnnotation: null
});
});
}
default:
{
throw (0, _createSyntaxError.createSyntaxError)(param, `Unhandled ${param.argument.type} encountered in restParameter`);
}
}
}
case 'ComponentParameter':
{
let value;
if (param.local.type === 'AssignmentPattern') {
value = nodeWith(param.local, {
left: nodeWith(param.local.left, {
typeAnnotation: null,
optional: false
})
});
} else {
value = nodeWith(param.local, {
typeAnnotation: null,
optional: false
});
} // Shorthand params
if (param.name.type === 'Identifier' && param.shorthand && (value.type === 'Identifier' || value.type === 'AssignmentPattern')) {
return [{
type: 'Property',
key: param.name,
kind: 'init',
value,
method: false,
shorthand: true,
computed: false,
loc: param.loc,
range: param.range,
parent: EMPTY_PARENT
}];
} // Complex params
return [{
type: 'Property',
key: param.name,
kind: 'init',
value,
method: false,
shorthand: false,
computed: false,
loc: param.loc,
range: param.range,
parent: EMPTY_PARENT
}];
}
default:
{
throw (0, _createSyntaxError.createSyntaxError)(param, `Unknown Component parameter type of "${param.type}"`);
}
}
}
function createForwardRefWrapper(originalComponent) {
const internalCompId = {
type: 'Identifier',
name: `${originalComponent.id.name}_withRef`,
optional: false,
typeAnnotation: null,
loc: originalComponent.id.loc,
range: originalComponent.id.range,
parent: EMPTY_PARENT
};
return {
forwardRefStatement: {
type: 'VariableDeclaration',
kind: 'const',
declarations: [{
type: 'VariableDeclarator',
id: (0, _astNodeMutationHelpers.shallowCloneNode)(originalComponent.id),
init: {
type: 'CallExpression',
callee: {
type: 'MemberExpression',
object: {
type: 'Identifier',
name: 'React',
optional: false,
typeAnnotation: null,
loc: originalComponent.loc,
range: originalComponent.range,
parent: EMPTY_PARENT
},
property: {
type: 'Identifier',
name: 'forwardRef',
optional: false,
typeAnnotation: null,
loc: originalComponent.loc,
range: originalComponent.range,
parent: EMPTY_PARENT
},
computed: false,
optional: false,
loc: originalComponent.loc,
range: originalComponent.range,
parent: EMPTY_PARENT
},
arguments: [(0, _astNodeMutationHelpers.shallowCloneNode)(internalCompId)],
typeArguments: null,
optional: false,
loc: originalComponent.loc,
range: originalComponent.range,
parent: EMPTY_PARENT
},
loc: originalComponent.loc,
range: originalComponent.range,
parent: EMPTY_PARENT
}],
loc: originalComponent.loc,
range: originalComponent.range,
parent: originalComponent.parent
},
internalCompId: internalCompId,
forwardRefCompId: originalComponent.id
};
}
function mapComponentDeclaration(node, options) {
// Create empty loc for return type annotation nodes
const createRendersTypeLoc = () => ({
loc: {
start: node.body.loc.end,
end: node.body.loc.end
},
range: [node.body.range[1], node.body.range[1]],
parent: EMPTY_PARENT
});
const returnType = {
type: 'TypeAnnotation',
typeAnnotation: {
type: 'GenericTypeAnnotation',
id: {
type: 'QualifiedTypeIdentifier',
qualification: {
type: 'Identifier',
name: 'React',
optional: false,
typeAnnotation: null,
...createRendersTypeLoc()
},
id: {
type: 'Identifier',
name: 'Node',
optional: false,
typeAnnotation: null,
...createRendersTypeLoc()
},
...createRendersTypeLoc()
},
typeParameters: null,
...createRendersTypeLoc()
},
...createRendersTypeLoc()
};
const {
props,
ref
} = mapComponentParameters(node.params, options);
let forwardRefDetails = null;
if (ref != null) {
forwardRefDetails = createForwardRefWrapper(node);
}
const comp = {
type: 'FunctionDeclaration',
id: forwardRefDetails != null ? (0, _astNodeMutationHelpers.shallowCloneNode)(forwardRefDetails.internalCompId) : (0, _astNodeMutationHelpers.shallowCloneNode)(node.id),
__componentDeclaration: true,
typeParameters: node.typeParameters,
params: props == null ? [] : ref == null ? [props] : [props, ref],
returnType,
body: node.body,
async: false,
generator: false,
predicate: null,
loc: node.loc,
range: node.range,
parent: node.parent
};
return {
comp,
forwardRefDetails
};
}
function mapDeclareHook(node) {
return {
type: 'DeclareFunction',
id: {
type: 'Identifier',
name: node.id.name,
optional: node.id.optional,
typeAnnotation: {
type: 'TypeAnnotation',
typeAnnotation: {
type: 'FunctionTypeAnnotation',
this: null,
params: node.id.typeAnnotation.typeAnnotation.params,
typeParameters: node.id.typeAnnotation.typeAnnotation.typeParameters,
rest: node.id.typeAnnotation.typeAnnotation.rest,
returnType: node.id.typeAnnotation.typeAnnotation.returnType,
loc: node.id.typeAnnotation.typeAnnotation.loc,
range: node.id.typeAnnotation.typeAnnotation.range,
parent: node.id.typeAnnotation.typeAnnotation.parent
},
loc: node.id.typeAnnotation.loc,
range: node.id.typeAnnotation.range,
parent: node.id.typeAnnotation.parent
},
loc: node.id.loc,
range: node.id.range,
parent: node.id.parent
},
loc: node.loc,
range: node.range,
parent: node.parent,
predicate: null
};
}
function mapHookDeclaration(node) {
const comp = {
type: 'FunctionDeclaration',
id: node.id && (0, _astNodeMutationHelpers.shallowCloneNode)(node.id),
__hookDeclaration: true,
typeParameters: node.typeParameters,
params: node.params,
returnType: node.returnType,
body: node.body,
async: false,
generator: false,
predicate: null,
loc: node.loc,
range: node.range,
parent: node.parent
};
return comp;
}
/**
* Scan a list of statements and return the position of the
* first statement that contains a reference to a given component
* or null of no references were found.
*/
function scanForFirstComponentReference(compName, bodyList) {
for (let i = 0; i < bodyList.length; i++) {
const bodyNode = bodyList[i];
let referencePos = null;
_SimpleTraverser.SimpleTraverser.traverse(bodyNode, {
enter(node) {
switch (node.type) {
case 'Identifier':
{
if (node.name === compName) {
// We found a reference, record it and stop.
referencePos = i;
throw _SimpleTraverser.SimpleTraverser.Break;
}
}
}
},
leave(_node) {}
});
if (referencePos != null) {
return referencePos;
}
}
return null;
}
function mapComponentDeclarationIntoList(node, newBody, options, insertExport) {
const {
comp,
forwardRefDetails
} = mapComponentDeclaration(node, options);
if (forwardRefDetails != null) {
// Scan for references to our component.
const referencePos = scanForFirstComponentReference(forwardRefDetails.forwardRefCompId.name, newBody); // If a reference is found insert the forwardRef statement before it (to simulate function hoisting).
if (referencePos != null) {
newBody.splice(referencePos, 0, forwardRefDetails.forwardRefStatement);
} else {
newBody.push(forwardRefDetails.forwardRefStatement);
}
newBody.push(comp);
if (insertExport != null) {
newBody.push(insertExport(forwardRefDetails.forwardRefCompId));
}
return;
}
newBody.push(insertExport != null ? insertExport(comp) : comp);
}
function mapStatementList(stmts, options) {
const newBody = [];
for (const node of stmts) {
switch (node.type) {
case 'ComponentDeclaration':
{
mapComponentDeclarationIntoList(node, newBody, options);
break;
}
case 'HookDeclaration':
{
const decl = mapHookDeclaration(node);
newBody.push(decl);
break;
}
case 'ExportNamedDeclaration':
{
var _node$declaration, _node$declaration2;
if (((_node$declaration = node.declaration) == null ? void 0 : _node$declaration.type) === 'ComponentDeclaration') {
mapComponentDeclarationIntoList(node.declaration, newBody, options, componentOrRef => {
switch (componentOrRef.type) {
case 'FunctionDeclaration':
{
// No ref, so we can export the component directly.
return nodeWith(node, {
declaration: componentOrRef
});
}
case 'Identifier':
{
// If a ref is inserted, we should just export that id.
return {
type: 'ExportNamedDeclaration',
declaration: null,
specifiers: [{
type: 'ExportSpecifier',
exported: (0, _astNodeMutationHelpers.shallowCloneNode)(componentOrRef),
local: (0, _astNodeMutationHelpers.shallowCloneNode)(componentOrRef),
loc: node.loc,
range: node.range,
parent: EMPTY_PARENT
}],
exportKind: 'value',
source: null,
loc: node.loc,
range: node.range,
parent: node.parent
};
}
}
});
break;
}
if (((_node$declaration2 = node.declaration) == null ? void 0 : _node$declaration2.type) === 'HookDeclaration') {
const comp = mapHookDeclaration(node.declaration);
newBody.push(nodeWith(node, {
declaration: comp
}));
break;
}
newBody.push(node);
break;
}
case 'ExportDefaultDeclaration':
{
var _node$declaration3, _node$declaration4;
if (((_node$declaration3 = node.declaration) == null ? void 0 : _node$declaration3.type) === 'ComponentDeclaration') {
mapComponentDeclarationIntoList(node.declaration, newBody, options, componentOrRef => nodeWith(node, {
declaration: componentOrRef
}));
break;
}
if (((_node$declaration4 = node.declaration) == null ? void 0 : _node$declaration4.type) === 'HookDeclaration') {
const comp = mapHookDeclaration(node.declaration);
newBody.push(nodeWith(node, {
declaration: comp
}));
break;
}
newBody.push(node);
break;
}
default:
{
newBody.push(node);
}
}
}
return newBody;
}
function transformProgram(program, options) {
return _SimpleTransform.SimpleTransform.transformProgram(program, {
transform(node) {
switch (node.type) {
case 'DeclareComponent':
{
return mapDeclareComponent(node);
}
case 'DeclareHook':
{
return mapDeclareHook(node);
}
case 'Program':
case 'BlockStatement':
{
return nodeWith(node, {
body: mapStatementList(node.body, options)
});
}
case 'SwitchCase':
{
const consequent = mapStatementList(node.consequent, options);
return nodeWith(node, {
/* $FlowExpectedError[incompatible-call] We know `mapStatementList` will
not return `ModuleDeclaration` nodes if it is not passed any */
consequent
});
}
case 'ComponentDeclaration':
{
var _node$parent;
throw (0, _createSyntaxError.createSyntaxError)(node, `Components must be defined at the top level of a module or within a ` + `BlockStatement, instead got parent of "${(_node$parent = node.parent) == null ? void 0 : _node$parent.type}".`);
}
case 'HookDeclaration':
{
var _node$parent2;
throw (0, _createSyntaxError.createSyntaxError)(node, `Hooks must be defined at the top level of a module or within a ` + `BlockStatement, instead got parent of "${(_node$parent2 = node.parent) == null ? void 0 : _node$parent2.type}".`);
}
default:
{
return node;
}
}
}
});
}

View File

@@ -0,0 +1,863 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
/**
* This transforms component syntax (https://flow.org/en/docs/react/component-syntax/)
* and hook syntax (https://flow.org/en/docs/react/hook-syntax/).
*
* It is expected that all transforms create valid ESTree AST output. If
* the transform requires outputting Babel specific AST nodes then it
* should live in `ConvertESTreeToBabel.js`
*/
'use strict';
import type {ParserOptions} from '../ParserOptions';
import type {
Program,
ESNode,
DeclareComponent,
DeclareVariable,
ComponentDeclaration,
FunctionDeclaration,
TypeAnnotation,
ComponentParameter,
SourceLocation,
Position,
ObjectPattern,
Identifier,
Range,
RestElement,
DestructuringObjectProperty,
VariableDeclaration,
ModuleDeclaration,
DeclareHook,
DeclareFunction,
HookDeclaration,
Statement,
AssignmentPattern,
BindingName,
ObjectTypePropertySignature,
ObjectTypeSpreadProperty,
} from 'hermes-estree';
import {SimpleTransform} from '../transform/SimpleTransform';
import {shallowCloneNode} from '../transform/astNodeMutationHelpers';
import {SimpleTraverser} from '../traverse/SimpleTraverser';
import {createSyntaxError} from '../utils/createSyntaxError';
const nodeWith = SimpleTransform.nodeWith;
// Rely on the mapper to fix up parent relationships.
const EMPTY_PARENT: $FlowFixMe = null;
function createDefaultPosition(): Position {
return {
line: 1,
column: 0,
};
}
function mapDeclareComponent(node: DeclareComponent): DeclareVariable {
return {
type: 'DeclareVariable',
id: nodeWith(node.id, {
typeAnnotation: {
type: 'TypeAnnotation',
typeAnnotation: {
type: 'AnyTypeAnnotation',
loc: node.loc,
range: node.range,
parent: EMPTY_PARENT,
},
loc: node.loc,
range: node.range,
parent: EMPTY_PARENT,
},
}),
kind: 'const',
loc: node.loc,
range: node.range,
parent: node.parent,
};
}
function getComponentParameterName(
paramName: ComponentParameter['name'],
): string {
switch (paramName.type) {
case 'Identifier':
return paramName.name;
case 'Literal':
return paramName.value;
default:
throw createSyntaxError(
paramName,
`Unknown Component parameter name type of "${paramName.type}"`,
);
}
}
function createPropsTypeAnnotation(
propTypes: Array<ObjectTypePropertySignature>,
spread: ?ObjectTypeSpreadProperty,
loc: ?SourceLocation,
range: ?Range,
): TypeAnnotation {
// Create empty loc for type annotation nodes
const createParamsTypeLoc = () => ({
loc: {
start: loc?.start != null ? loc.start : createDefaultPosition(),
end: loc?.end != null ? loc.end : createDefaultPosition(),
},
range: range ?? [0, 0],
parent: EMPTY_PARENT,
});
// Optimize `{...Props}` -> `Props`
if (spread != null && propTypes.length === 0) {
return {
type: 'TypeAnnotation',
typeAnnotation: spread.argument,
...createParamsTypeLoc(),
};
}
const typeProperties: Array<
ObjectTypePropertySignature | ObjectTypeSpreadProperty,
> = [...propTypes];
if (spread != null) {
// Spread needs to be the first type, as inline properties take precedence.
typeProperties.unshift(spread);
}
const propTypeObj = {
type: 'ObjectTypeAnnotation',
callProperties: [],
properties: typeProperties,
indexers: [],
internalSlots: [],
exact: false,
inexact: false,
...createParamsTypeLoc(),
};
return {
type: 'TypeAnnotation',
typeAnnotation: {
type: 'GenericTypeAnnotation',
id: {
type: 'Identifier',
name: '$ReadOnly',
optional: false,
typeAnnotation: null,
...createParamsTypeLoc(),
},
typeParameters: {
type: 'TypeParameterInstantiation',
params: [propTypeObj],
...createParamsTypeLoc(),
},
...createParamsTypeLoc(),
},
...createParamsTypeLoc(),
};
}
function mapComponentParameters(
params: $ReadOnlyArray<ComponentParameter | RestElement>,
options: ParserOptions,
): $ReadOnly<{
props: ?(ObjectPattern | Identifier),
ref: ?(BindingName | AssignmentPattern),
}> {
if (params.length === 0) {
return {props: null, ref: null};
}
// Optimize `component Foo(...props: Props) {}` to `function Foo(props: Props) {}
if (
params.length === 1 &&
params[0].type === 'RestElement' &&
params[0].argument.type === 'Identifier'
) {
const restElementArgument = params[0].argument;
return {
props: restElementArgument,
ref: null,
};
}
// Filter out any ref param and capture it's details when targeting React 18.
// React 19+ treats ref as a regular prop for function components.
let refParam = null;
const paramsWithoutRef =
(options.reactRuntimeTarget ?? '18') === '18'
? params.filter(param => {
if (
param.type === 'ComponentParameter' &&
getComponentParameterName(param.name) === 'ref'
) {
refParam = param;
return false;
}
return true;
})
: params;
const [propTypes, spread] = paramsWithoutRef.reduce<
[Array<ObjectTypePropertySignature>, ?ObjectTypeSpreadProperty],
>(
([propTypes, spread], param) => {
switch (param.type) {
case 'RestElement': {
if (spread != null) {
throw createSyntaxError(
param,
`Invalid state, multiple rest elements found as Component Parameters`,
);
}
return [propTypes, mapComponentParameterRestElementType(param)];
}
case 'ComponentParameter': {
propTypes.push(mapComponentParameterType(param));
return [propTypes, spread];
}
}
},
[[], null],
);
const propsProperties = paramsWithoutRef.flatMap(mapComponentParameter);
let props = null;
if (propsProperties.length === 0) {
if (refParam == null) {
throw new Error(
'StripComponentSyntax: Invalid state, ref should always be set at this point if props are empty',
);
}
const emptyParamsLoc = {
start: refParam.loc.start,
end: refParam.loc.start,
};
const emptyParamsRange = [refParam.range[0], refParam.range[0]];
// no properties provided (must have had a single ref)
props = {
type: 'Identifier',
name: '_$$empty_props_placeholder$$',
optional: false,
typeAnnotation: createPropsTypeAnnotation(
[],
null,
emptyParamsLoc,
emptyParamsRange,
),
loc: emptyParamsLoc,
range: emptyParamsRange,
parent: EMPTY_PARENT,
};
} else {
const lastPropsProperty = propsProperties[propsProperties.length - 1];
props = {
type: 'ObjectPattern',
properties: propsProperties,
typeAnnotation: createPropsTypeAnnotation(
propTypes,
spread,
{
start: lastPropsProperty.loc.end,
end: lastPropsProperty.loc.end,
},
[lastPropsProperty.range[1], lastPropsProperty.range[1]],
),
loc: {
start: propsProperties[0].loc.start,
end: lastPropsProperty.loc.end,
},
range: [propsProperties[0].range[0], lastPropsProperty.range[1]],
parent: EMPTY_PARENT,
};
}
let ref = null;
if (refParam != null) {
ref = refParam.local;
}
return {
props,
ref,
};
}
function mapComponentParameterType(
param: ComponentParameter,
): ObjectTypePropertySignature {
const typeAnnotation =
param.local.type === 'AssignmentPattern'
? param.local.left.typeAnnotation
: param.local.typeAnnotation;
const optional =
param.local.type === 'AssignmentPattern'
? true
: param.local.type === 'Identifier'
? param.local.optional
: false;
return {
type: 'ObjectTypeProperty',
key: shallowCloneNode(param.name),
value: typeAnnotation?.typeAnnotation ?? {
type: 'AnyTypeAnnotation',
loc: param.local.loc,
range: param.local.range,
parent: EMPTY_PARENT,
},
kind: 'init',
optional,
method: false,
static: false,
proto: false,
variance: null,
loc: param.local.loc,
range: param.local.range,
parent: EMPTY_PARENT,
};
}
function mapComponentParameterRestElementType(
param: RestElement,
): ObjectTypeSpreadProperty {
if (
param.argument.type !== 'Identifier' &&
param.argument.type !== 'ObjectPattern'
) {
throw createSyntaxError(
param,
`Invalid ${param.argument.type} encountered in restParameter`,
);
}
return {
type: 'ObjectTypeSpreadProperty',
argument: param.argument.typeAnnotation?.typeAnnotation ?? {
type: 'AnyTypeAnnotation',
loc: param.loc,
range: param.range,
parent: EMPTY_PARENT,
},
loc: param.loc,
range: param.range,
parent: EMPTY_PARENT,
};
}
function mapComponentParameter(
param: ComponentParameter | RestElement,
): Array<DestructuringObjectProperty | RestElement> {
switch (param.type) {
case 'RestElement': {
switch (param.argument.type) {
case 'Identifier': {
const a = nodeWith(param, {
typeAnnotation: null,
argument: nodeWith(param.argument, {typeAnnotation: null}),
});
return [a];
}
case 'ObjectPattern': {
return param.argument.properties.map(property => {
return nodeWith(property, {
typeAnnotation: null,
});
});
}
default: {
throw createSyntaxError(
param,
`Unhandled ${param.argument.type} encountered in restParameter`,
);
}
}
}
case 'ComponentParameter': {
let value;
if (param.local.type === 'AssignmentPattern') {
value = nodeWith(param.local, {
left: nodeWith(param.local.left, {
typeAnnotation: null,
optional: false,
}),
});
} else {
value = nodeWith(param.local, {
typeAnnotation: null,
optional: false,
});
}
// Shorthand params
if (
param.name.type === 'Identifier' &&
param.shorthand &&
(value.type === 'Identifier' || value.type === 'AssignmentPattern')
) {
return [
{
type: 'Property',
key: param.name,
kind: 'init',
value,
method: false,
shorthand: true,
computed: false,
loc: param.loc,
range: param.range,
parent: EMPTY_PARENT,
},
];
}
// Complex params
return [
{
type: 'Property',
key: param.name,
kind: 'init',
value,
method: false,
shorthand: false,
computed: false,
loc: param.loc,
range: param.range,
parent: EMPTY_PARENT,
},
];
}
default: {
throw createSyntaxError(
param,
`Unknown Component parameter type of "${param.type}"`,
);
}
}
}
type ForwardRefDetails = {
forwardRefStatement: VariableDeclaration,
internalCompId: Identifier,
forwardRefCompId: Identifier,
};
function createForwardRefWrapper(
originalComponent: ComponentDeclaration,
): ForwardRefDetails {
const internalCompId = {
type: 'Identifier',
name: `${originalComponent.id.name}_withRef`,
optional: false,
typeAnnotation: null,
loc: originalComponent.id.loc,
range: originalComponent.id.range,
parent: EMPTY_PARENT,
};
return {
forwardRefStatement: {
type: 'VariableDeclaration',
kind: 'const',
declarations: [
{
type: 'VariableDeclarator',
id: shallowCloneNode(originalComponent.id),
init: {
type: 'CallExpression',
callee: {
type: 'MemberExpression',
object: {
type: 'Identifier',
name: 'React',
optional: false,
typeAnnotation: null,
loc: originalComponent.loc,
range: originalComponent.range,
parent: EMPTY_PARENT,
},
property: {
type: 'Identifier',
name: 'forwardRef',
optional: false,
typeAnnotation: null,
loc: originalComponent.loc,
range: originalComponent.range,
parent: EMPTY_PARENT,
},
computed: false,
optional: false,
loc: originalComponent.loc,
range: originalComponent.range,
parent: EMPTY_PARENT,
},
arguments: [shallowCloneNode(internalCompId)],
typeArguments: null,
optional: false,
loc: originalComponent.loc,
range: originalComponent.range,
parent: EMPTY_PARENT,
},
loc: originalComponent.loc,
range: originalComponent.range,
parent: EMPTY_PARENT,
},
],
loc: originalComponent.loc,
range: originalComponent.range,
parent: originalComponent.parent,
},
internalCompId: internalCompId,
forwardRefCompId: originalComponent.id,
};
}
function mapComponentDeclaration(
node: ComponentDeclaration,
options: ParserOptions,
): {
comp: FunctionDeclaration,
forwardRefDetails: ?ForwardRefDetails,
} {
// Create empty loc for return type annotation nodes
const createRendersTypeLoc = () => ({
loc: {
start: node.body.loc.end,
end: node.body.loc.end,
},
range: [node.body.range[1], node.body.range[1]],
parent: EMPTY_PARENT,
});
const returnType: TypeAnnotation = {
type: 'TypeAnnotation',
typeAnnotation: {
type: 'GenericTypeAnnotation',
id: {
type: 'QualifiedTypeIdentifier',
qualification: {
type: 'Identifier',
name: 'React',
optional: false,
typeAnnotation: null,
...createRendersTypeLoc(),
},
id: {
type: 'Identifier',
name: 'Node',
optional: false,
typeAnnotation: null,
...createRendersTypeLoc(),
},
...createRendersTypeLoc(),
},
typeParameters: null,
...createRendersTypeLoc(),
},
...createRendersTypeLoc(),
};
const {props, ref} = mapComponentParameters(node.params, options);
let forwardRefDetails: ?ForwardRefDetails = null;
if (ref != null) {
forwardRefDetails = createForwardRefWrapper(node);
}
const comp = {
type: 'FunctionDeclaration',
id:
forwardRefDetails != null
? shallowCloneNode(forwardRefDetails.internalCompId)
: shallowCloneNode(node.id),
__componentDeclaration: true,
typeParameters: node.typeParameters,
params: props == null ? [] : ref == null ? [props] : [props, ref],
returnType,
body: node.body,
async: false,
generator: false,
predicate: null,
loc: node.loc,
range: node.range,
parent: node.parent,
};
return {comp, forwardRefDetails};
}
function mapDeclareHook(node: DeclareHook): DeclareFunction {
return {
type: 'DeclareFunction',
id: {
type: 'Identifier',
name: node.id.name,
optional: node.id.optional,
typeAnnotation: {
type: 'TypeAnnotation',
typeAnnotation: {
type: 'FunctionTypeAnnotation',
this: null,
params: node.id.typeAnnotation.typeAnnotation.params,
typeParameters: node.id.typeAnnotation.typeAnnotation.typeParameters,
rest: node.id.typeAnnotation.typeAnnotation.rest,
returnType: node.id.typeAnnotation.typeAnnotation.returnType,
loc: node.id.typeAnnotation.typeAnnotation.loc,
range: node.id.typeAnnotation.typeAnnotation.range,
parent: node.id.typeAnnotation.typeAnnotation.parent,
},
loc: node.id.typeAnnotation.loc,
range: node.id.typeAnnotation.range,
parent: node.id.typeAnnotation.parent,
},
loc: node.id.loc,
range: node.id.range,
parent: node.id.parent,
},
loc: node.loc,
range: node.range,
parent: node.parent,
predicate: null,
};
}
function mapHookDeclaration(node: HookDeclaration): FunctionDeclaration {
const comp = {
type: 'FunctionDeclaration',
id: node.id && shallowCloneNode(node.id),
__hookDeclaration: true,
typeParameters: node.typeParameters,
params: node.params,
returnType: node.returnType,
body: node.body,
async: false,
generator: false,
predicate: null,
loc: node.loc,
range: node.range,
parent: node.parent,
};
return comp;
}
/**
* Scan a list of statements and return the position of the
* first statement that contains a reference to a given component
* or null of no references were found.
*/
function scanForFirstComponentReference(
compName: string,
bodyList: Array<Statement | ModuleDeclaration>,
): ?number {
for (let i = 0; i < bodyList.length; i++) {
const bodyNode = bodyList[i];
let referencePos = null;
SimpleTraverser.traverse(bodyNode, {
enter(node: ESNode) {
switch (node.type) {
case 'Identifier': {
if (node.name === compName) {
// We found a reference, record it and stop.
referencePos = i;
throw SimpleTraverser.Break;
}
}
}
},
leave(_node: ESNode) {},
});
if (referencePos != null) {
return referencePos;
}
}
return null;
}
function mapComponentDeclarationIntoList(
node: ComponentDeclaration,
newBody: Array<Statement | ModuleDeclaration>,
options: ParserOptions,
insertExport?: (Identifier | FunctionDeclaration) => ModuleDeclaration,
) {
const {comp, forwardRefDetails} = mapComponentDeclaration(node, options);
if (forwardRefDetails != null) {
// Scan for references to our component.
const referencePos = scanForFirstComponentReference(
forwardRefDetails.forwardRefCompId.name,
newBody,
);
// If a reference is found insert the forwardRef statement before it (to simulate function hoisting).
if (referencePos != null) {
newBody.splice(referencePos, 0, forwardRefDetails.forwardRefStatement);
} else {
newBody.push(forwardRefDetails.forwardRefStatement);
}
newBody.push(comp);
if (insertExport != null) {
newBody.push(insertExport(forwardRefDetails.forwardRefCompId));
}
return;
}
newBody.push(insertExport != null ? insertExport(comp) : comp);
}
function mapStatementList(
stmts: $ReadOnlyArray<Statement | ModuleDeclaration>,
options: ParserOptions,
) {
const newBody: Array<Statement | ModuleDeclaration> = [];
for (const node of stmts) {
switch (node.type) {
case 'ComponentDeclaration': {
mapComponentDeclarationIntoList(node, newBody, options);
break;
}
case 'HookDeclaration': {
const decl = mapHookDeclaration(node);
newBody.push(decl);
break;
}
case 'ExportNamedDeclaration': {
if (node.declaration?.type === 'ComponentDeclaration') {
mapComponentDeclarationIntoList(
node.declaration,
newBody,
options,
componentOrRef => {
switch (componentOrRef.type) {
case 'FunctionDeclaration': {
// No ref, so we can export the component directly.
return nodeWith(node, {declaration: componentOrRef});
}
case 'Identifier': {
// If a ref is inserted, we should just export that id.
return {
type: 'ExportNamedDeclaration',
declaration: null,
specifiers: [
{
type: 'ExportSpecifier',
exported: shallowCloneNode(componentOrRef),
local: shallowCloneNode(componentOrRef),
loc: node.loc,
range: node.range,
parent: EMPTY_PARENT,
},
],
exportKind: 'value',
source: null,
loc: node.loc,
range: node.range,
parent: node.parent,
};
}
}
},
);
break;
}
if (node.declaration?.type === 'HookDeclaration') {
const comp = mapHookDeclaration(node.declaration);
newBody.push(nodeWith(node, {declaration: comp}));
break;
}
newBody.push(node);
break;
}
case 'ExportDefaultDeclaration': {
if (node.declaration?.type === 'ComponentDeclaration') {
mapComponentDeclarationIntoList(
node.declaration,
newBody,
options,
componentOrRef => nodeWith(node, {declaration: componentOrRef}),
);
break;
}
if (node.declaration?.type === 'HookDeclaration') {
const comp = mapHookDeclaration(node.declaration);
newBody.push(nodeWith(node, {declaration: comp}));
break;
}
newBody.push(node);
break;
}
default: {
newBody.push(node);
}
}
}
return newBody;
}
export function transformProgram(
program: Program,
options: ParserOptions,
): Program {
return SimpleTransform.transformProgram(program, {
transform(node: ESNode) {
switch (node.type) {
case 'DeclareComponent': {
return mapDeclareComponent(node);
}
case 'DeclareHook': {
return mapDeclareHook(node);
}
case 'Program':
case 'BlockStatement': {
return nodeWith(node, {body: mapStatementList(node.body, options)});
}
case 'SwitchCase': {
const consequent = mapStatementList(node.consequent, options);
return nodeWith(node, {
/* $FlowExpectedError[incompatible-call] We know `mapStatementList` will
not return `ModuleDeclaration` nodes if it is not passed any */
consequent,
});
}
case 'ComponentDeclaration': {
throw createSyntaxError(
node,
`Components must be defined at the top level of a module or within a ` +
`BlockStatement, instead got parent of "${node.parent?.type}".`,
);
}
case 'HookDeclaration': {
throw createSyntaxError(
node,
`Hooks must be defined at the top level of a module or within a ` +
`BlockStatement, instead got parent of "${node.parent?.type}".`,
);
}
default: {
return node;
}
}
},
});
}

View File

@@ -0,0 +1,175 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
/**
* This transform strips all Flow types.
*
* It is expected that all transforms create valid ESTree AST output. If
* the transform requires outputting Babel specific AST nodes then it
* should live in `ConvertESTreeToBabel.js`
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.transformProgram = transformProgram;
var _SimpleTransform = require("../transform/SimpleTransform");
const nodeWith = _SimpleTransform.SimpleTransform.nodeWith;
function transformProgram(program, _options) {
return _SimpleTransform.SimpleTransform.transformProgram(program, {
transform(node) {
switch (node.type) {
case 'AsExpression':
case 'AsConstExpression':
case 'TypeCastExpression':
{
return node.expression;
}
case 'CallExpression':
case 'NewExpression':
{
if (node.typeArguments != null) {
return nodeWith(node, {
typeArguments: null
});
}
return node;
}
case 'ObjectPattern':
case 'ArrayPattern':
case 'Identifier':
{
if (node.typeAnnotation != null) {
return nodeWith(node, {
typeAnnotation: null
});
}
return node;
}
case 'DeclareClass':
case 'DeclareFunction':
case 'DeclareInterface':
case 'DeclareModule':
case 'DeclareModuleExports':
case 'DeclareNamespace':
case 'DeclareOpaqueType':
case 'DeclareTypeAlias':
case 'DeclareVariable':
case 'InterfaceDeclaration':
case 'OpaqueType':
case 'TypeAlias':
{
return null;
}
case 'FunctionDeclaration':
case 'ArrowFunctionExpression':
case 'FunctionExpression':
{
const newParams = [];
for (let i = 0; i < node.params.length; i++) {
if (i === 0 && node.params[0].type === 'Identifier' && node.params[0].name === 'this') {
continue;
}
let param = node.params[i];
if (param.type === 'AssignmentPattern') {
param = param.left;
}
if (param.optional === true) {
param = nodeWith(param, {
optional: false
});
}
newParams.push(param);
}
return nodeWith(node, {
params: newParams,
returnType: null,
typeParameters: null,
predicate: null
});
}
case 'ClassDeclaration':
case 'ClassExpression':
{
return nodeWith(node, {
typeParameters: null,
superTypeParameters: null,
implements: [],
decorators: []
});
}
case 'PropertyDefinition':
{
return nodeWith(node, {
typeAnnotation: null,
variance: null,
declare: false,
optional: false
});
}
case 'ImportDeclaration':
{
if (node.importKind === 'type' || node.importKind === 'typeof') {
return null;
}
const nonTypeSpecifiers = node.specifiers.filter(s => s.type !== 'ImportSpecifier' || s.importKind !== 'type' && s.importKind !== 'typeof');
if (nonTypeSpecifiers.length === 0) {
return null;
}
if (nonTypeSpecifiers.length === node.specifiers.length) {
return node;
}
return nodeWith(node, {
specifiers: nonTypeSpecifiers
});
}
case 'ExportAllDeclaration':
case 'ExportNamedDeclaration':
{
if (node.exportKind === 'type') {
return null;
}
return node;
}
default:
{
return node;
}
}
}
});
}

View File

@@ -0,0 +1,158 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
/**
* This transform strips all Flow types.
*
* It is expected that all transforms create valid ESTree AST output. If
* the transform requires outputting Babel specific AST nodes then it
* should live in `ConvertESTreeToBabel.js`
*/
'use strict';
import type {ParserOptions} from '../ParserOptions';
import type {Program, ESNode} from 'hermes-estree';
import {SimpleTransform} from '../transform/SimpleTransform';
const nodeWith = SimpleTransform.nodeWith;
export function transformProgram(
program: Program,
_options: ParserOptions,
): Program {
return SimpleTransform.transformProgram(program, {
transform(node: ESNode) {
switch (node.type) {
case 'AsExpression':
case 'AsConstExpression':
case 'TypeCastExpression': {
return node.expression;
}
case 'CallExpression':
case 'NewExpression': {
if (node.typeArguments != null) {
return nodeWith(node, {typeArguments: null});
}
return node;
}
case 'ObjectPattern':
case 'ArrayPattern':
case 'Identifier': {
if (node.typeAnnotation != null) {
return nodeWith(node, {typeAnnotation: null});
}
return node;
}
case 'DeclareClass':
case 'DeclareFunction':
case 'DeclareInterface':
case 'DeclareModule':
case 'DeclareModuleExports':
case 'DeclareNamespace':
case 'DeclareOpaqueType':
case 'DeclareTypeAlias':
case 'DeclareVariable':
case 'InterfaceDeclaration':
case 'OpaqueType':
case 'TypeAlias': {
return null;
}
case 'FunctionDeclaration':
case 'ArrowFunctionExpression':
case 'FunctionExpression': {
const newParams = [];
for (let i = 0; i < node.params.length; i++) {
if (
i === 0 &&
node.params[0].type === 'Identifier' &&
node.params[0].name === 'this'
) {
continue;
}
let param = node.params[i];
if (param.type === 'AssignmentPattern') {
param = param.left;
}
if (param.optional === true) {
param = nodeWith(param, {optional: false});
}
newParams.push(param);
}
return nodeWith(node, {
params: newParams,
returnType: null,
typeParameters: null,
predicate: null,
});
}
case 'ClassDeclaration':
case 'ClassExpression': {
return nodeWith(node, {
typeParameters: null,
superTypeParameters: null,
implements: [],
decorators: [],
});
}
case 'PropertyDefinition': {
return nodeWith(node, {
typeAnnotation: null,
variance: null,
declare: false,
optional: false,
});
}
case 'ImportDeclaration': {
if (node.importKind === 'type' || node.importKind === 'typeof') {
return null;
}
const nonTypeSpecifiers = node.specifiers.filter(
s =>
s.type !== 'ImportSpecifier' ||
(s.importKind !== 'type' && s.importKind !== 'typeof'),
);
if (nonTypeSpecifiers.length === 0) {
return null;
}
if (nonTypeSpecifiers.length === node.specifiers.length) {
return node;
}
return nodeWith(node, {
specifiers: nonTypeSpecifiers,
});
}
case 'ExportAllDeclaration':
case 'ExportNamedDeclaration': {
if (node.exportKind === 'type') {
return null;
}
return node;
}
default: {
return node;
}
}
},
});
}

View File

@@ -0,0 +1,215 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
/**
* This transform strips Flow types that are not supported past Babel 7.
*
* It is expected that all transforms create valid ESTree AST output. If
* the transform requires outputting Babel specific AST nodes then it
* should live in `ConvertESTreeToBabel.js`
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.transformProgram = transformProgram;
var _SimpleTransform = require("../transform/SimpleTransform");
var _createSyntaxError = require("../utils/createSyntaxError");
const nodeWith = _SimpleTransform.SimpleTransform.nodeWith; // Rely on the mapper to fix up parent relationships.
const EMPTY_PARENT = null;
function createSimpleGenericTypeAnnotation(name, nodeForLoc) {
return {
type: 'GenericTypeAnnotation',
id: {
type: 'Identifier',
name,
optional: false,
typeAnnotation: null,
loc: nodeForLoc.loc,
range: nodeForLoc.range,
parent: EMPTY_PARENT
},
typeParameters: null,
loc: nodeForLoc.loc,
range: nodeForLoc.range,
parent: nodeForLoc.parent
};
}
function createAnyTypeAnnotation(node) {
return {
type: 'AnyTypeAnnotation',
loc: node.loc,
range: node.range,
parent: node.parent
};
}
/**
* Convert DeclareEnum nodes to DeclareVariable
*/
function mapDeclareEnum(node) {
return {
type: 'DeclareVariable',
kind: 'const',
id: nodeWith(node.id, {
typeAnnotation: {
type: 'TypeAnnotation',
typeAnnotation: createAnyTypeAnnotation(node.body),
loc: node.body.loc,
range: node.body.range,
parent: EMPTY_PARENT
}
}),
loc: node.loc,
range: node.range,
parent: node.parent
};
}
/**
* Convert DeclareNamespace nodes to DeclareVariable
*/
function mapDeclareNamespace(node) {
return {
type: 'DeclareVariable',
kind: 'const',
id: nodeWith(node.id, {
typeAnnotation: {
type: 'TypeAnnotation',
typeAnnotation: createAnyTypeAnnotation(node.body),
loc: node.body.loc,
range: node.body.range,
parent: EMPTY_PARENT
}
}),
loc: node.loc,
range: node.range,
parent: node.parent
};
}
/**
* Remove `this` param from functions.
*/
function mapFunction(node) {
// Remove the first parameter if it is a this-type annotation,
// which is not recognized by Babel.
if (node.params.length !== 0 && node.params[0].name === 'this') {
return nodeWith(node, {
params: node.params.slice(1)
});
}
return node;
}
/**
* Convert to QualifiedTypeIdentifier
*/
function mapQualifiedTypeofIdentifier(node) {
return {
type: 'QualifiedTypeIdentifier',
qualification: node.qualification.type === 'QualifiedTypeofIdentifier' ? mapQualifiedTypeofIdentifier(node.qualification) : node.qualification,
id: node.id,
loc: node.loc,
range: node.range,
parent: node.parent
};
}
function transformProgram(program, _options) {
return _SimpleTransform.SimpleTransform.transformProgram(program, {
transform(node) {
switch (node.type) {
case 'SymbolTypeAnnotation':
{
// Convert to simple generic type annotation
return createSimpleGenericTypeAnnotation('symbol', node);
}
case 'BigIntTypeAnnotation':
{
// Convert to simple generic type annotation
return createSimpleGenericTypeAnnotation('bigint', node);
}
case 'ObjectTypeAnnotation':
{
const shouldStrip = node.properties.some(prop => prop.type === 'ObjectTypeMappedTypeProperty');
if (shouldStrip) {
return createAnyTypeAnnotation(node);
}
return node;
}
case 'ObjectTypeMappedTypeProperty':
{
throw (0, _createSyntaxError.createSyntaxError)(node, `Invalid AST structure, ObjectTypeMappedTypeProperty found outside of an ObjectTypeAnnotation`);
}
case 'IndexedAccessType':
case 'OptionalIndexedAccessType':
case 'KeyofTypeAnnotation':
case 'ConditionalTypeAnnotation':
case 'InferTypeAnnotation':
case 'TupleTypeLabeledElement':
case 'TupleTypeSpreadElement':
case 'ComponentTypeAnnotation':
case 'HookTypeAnnotation':
case 'TypeOperator':
case 'TypePredicate':
{
// Babel does not support these generic types, so convert to any
return createAnyTypeAnnotation(node);
}
case 'QualifiedTypeofIdentifier':
{
return mapQualifiedTypeofIdentifier(node);
}
case 'DeclareEnum':
{
return mapDeclareEnum(node);
}
case 'DeclareNamespace':
{
return mapDeclareNamespace(node);
}
case 'FunctionDeclaration':
case 'FunctionExpression':
{
return mapFunction(node);
}
default:
{
return node;
}
}
}
});
}

View File

@@ -0,0 +1,216 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
/**
* This transform strips Flow types that are not supported past Babel 7.
*
* It is expected that all transforms create valid ESTree AST output. If
* the transform requires outputting Babel specific AST nodes then it
* should live in `ConvertESTreeToBabel.js`
*/
'use strict';
import type {ParserOptions} from '../ParserOptions';
import type {
Program,
ESNode,
DeclareEnum,
DeclareNamespace,
DeclareVariable,
AnyTypeAnnotation,
GenericTypeAnnotation,
QualifiedTypeIdentifier,
QualifiedTypeofIdentifier,
AFunction,
} from 'hermes-estree';
import {SimpleTransform} from '../transform/SimpleTransform';
import {createSyntaxError} from '../utils/createSyntaxError';
const nodeWith = SimpleTransform.nodeWith;
// Rely on the mapper to fix up parent relationships.
const EMPTY_PARENT: $FlowFixMe = null;
function createSimpleGenericTypeAnnotation(
name: string,
nodeForLoc: ESNode,
): GenericTypeAnnotation {
return {
type: 'GenericTypeAnnotation',
id: {
type: 'Identifier',
name,
optional: false,
typeAnnotation: null,
loc: nodeForLoc.loc,
range: nodeForLoc.range,
parent: EMPTY_PARENT,
},
typeParameters: null,
loc: nodeForLoc.loc,
range: nodeForLoc.range,
parent: nodeForLoc.parent,
};
}
function createAnyTypeAnnotation(node: ESNode): AnyTypeAnnotation {
return {
type: 'AnyTypeAnnotation',
loc: node.loc,
range: node.range,
parent: node.parent,
};
}
/**
* Convert DeclareEnum nodes to DeclareVariable
*/
function mapDeclareEnum(node: DeclareEnum): DeclareVariable {
return {
type: 'DeclareVariable',
kind: 'const',
id: nodeWith(node.id, {
typeAnnotation: {
type: 'TypeAnnotation',
typeAnnotation: createAnyTypeAnnotation(node.body),
loc: node.body.loc,
range: node.body.range,
parent: EMPTY_PARENT,
},
}),
loc: node.loc,
range: node.range,
parent: node.parent,
};
}
/**
* Convert DeclareNamespace nodes to DeclareVariable
*/
function mapDeclareNamespace(node: DeclareNamespace): DeclareVariable {
return {
type: 'DeclareVariable',
kind: 'const',
id: nodeWith(node.id, {
typeAnnotation: {
type: 'TypeAnnotation',
typeAnnotation: createAnyTypeAnnotation(node.body),
loc: node.body.loc,
range: node.body.range,
parent: EMPTY_PARENT,
},
}),
loc: node.loc,
range: node.range,
parent: node.parent,
};
}
/**
* Remove `this` param from functions.
*/
function mapFunction(node: AFunction): AFunction {
// Remove the first parameter if it is a this-type annotation,
// which is not recognized by Babel.
if (node.params.length !== 0 && node.params[0].name === 'this') {
return nodeWith(node, {
params: node.params.slice(1),
});
}
return node;
}
/**
* Convert to QualifiedTypeIdentifier
*/
function mapQualifiedTypeofIdentifier(
node: QualifiedTypeofIdentifier,
): QualifiedTypeIdentifier {
return {
type: 'QualifiedTypeIdentifier',
qualification:
node.qualification.type === 'QualifiedTypeofIdentifier'
? mapQualifiedTypeofIdentifier(node.qualification)
: node.qualification,
id: node.id,
loc: node.loc,
range: node.range,
parent: node.parent,
};
}
export function transformProgram(
program: Program,
_options: ParserOptions,
): Program {
return SimpleTransform.transformProgram(program, {
transform(node: ESNode) {
switch (node.type) {
case 'SymbolTypeAnnotation': {
// Convert to simple generic type annotation
return createSimpleGenericTypeAnnotation('symbol', node);
}
case 'BigIntTypeAnnotation': {
// Convert to simple generic type annotation
return createSimpleGenericTypeAnnotation('bigint', node);
}
case 'ObjectTypeAnnotation': {
const shouldStrip = node.properties.some(
prop => prop.type === 'ObjectTypeMappedTypeProperty',
);
if (shouldStrip) {
return createAnyTypeAnnotation(node);
}
return node;
}
case 'ObjectTypeMappedTypeProperty': {
throw createSyntaxError(
node,
`Invalid AST structure, ObjectTypeMappedTypeProperty found outside of an ObjectTypeAnnotation`,
);
}
case 'IndexedAccessType':
case 'OptionalIndexedAccessType':
case 'KeyofTypeAnnotation':
case 'ConditionalTypeAnnotation':
case 'InferTypeAnnotation':
case 'TupleTypeLabeledElement':
case 'TupleTypeSpreadElement':
case 'ComponentTypeAnnotation':
case 'HookTypeAnnotation':
case 'TypeOperator':
case 'TypePredicate': {
// Babel does not support these generic types, so convert to any
return createAnyTypeAnnotation(node);
}
case 'QualifiedTypeofIdentifier': {
return mapQualifiedTypeofIdentifier(node);
}
case 'DeclareEnum': {
return mapDeclareEnum(node);
}
case 'DeclareNamespace': {
return mapDeclareNamespace(node);
}
case 'FunctionDeclaration':
case 'FunctionExpression': {
return mapFunction(node);
}
default: {
return node;
}
}
},
});
}

View File

@@ -0,0 +1,203 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
* @generated
*/
/*
* !!! GENERATED FILE !!!
*
* Any manual changes to this file will be overwritten. To regenerate run `yarn build`.
*/
// lint directives to let us do some basic validation of generated files
/* eslint no-undef: 'error', no-unused-vars: ['error', {vars: "local"}], no-redeclare: 'error' */
/* global $NonMaybeType, Partial, $ReadOnly, $ReadOnlyArray, $FlowFixMe */
'use strict';
module.exports = {
AnyTypeAnnotation: [],
ArrayExpression: ['elements'],
ArrayPattern: ['elements', 'typeAnnotation'],
ArrayTypeAnnotation: ['elementType'],
ArrowFunctionExpression: ['id', 'params', 'body', 'typeParameters', 'returnType', 'predicate'],
AsConstExpression: ['expression'],
AsExpression: ['expression', 'typeAnnotation'],
AssignmentExpression: ['left', 'right'],
AssignmentPattern: ['left', 'right'],
AwaitExpression: ['argument'],
BigIntLiteralTypeAnnotation: [],
BigIntTypeAnnotation: [],
BinaryExpression: ['left', 'right'],
BlockStatement: ['body'],
BooleanLiteralTypeAnnotation: [],
BooleanTypeAnnotation: [],
BreakStatement: ['label'],
CallExpression: ['callee', 'typeArguments', 'arguments'],
CatchClause: ['param', 'body'],
ChainExpression: ['expression'],
ClassBody: ['body'],
ClassDeclaration: ['id', 'typeParameters', 'superClass', 'superTypeParameters', 'implements', 'decorators', 'body'],
ClassExpression: ['id', 'typeParameters', 'superClass', 'superTypeParameters', 'implements', 'decorators', 'body'],
ClassImplements: ['id', 'typeParameters'],
ComponentDeclaration: ['id', 'params', 'body', 'typeParameters', 'rendersType'],
ComponentParameter: ['name', 'local'],
ComponentTypeAnnotation: ['params', 'rest', 'typeParameters', 'rendersType'],
ComponentTypeParameter: ['name', 'typeAnnotation'],
ConditionalExpression: ['test', 'consequent', 'alternate'],
ConditionalTypeAnnotation: ['checkType', 'extendsType', 'trueType', 'falseType'],
ContinueStatement: ['label'],
DebuggerStatement: [],
DeclareClass: ['id', 'typeParameters', 'extends', 'implements', 'mixins', 'body'],
DeclareComponent: ['id', 'params', 'rest', 'typeParameters', 'rendersType'],
DeclaredPredicate: ['value'],
DeclareEnum: ['id', 'body'],
DeclareExportAllDeclaration: ['source'],
DeclareExportDeclaration: ['declaration', 'specifiers', 'source'],
DeclareFunction: ['id', 'predicate'],
DeclareHook: ['id'],
DeclareInterface: ['id', 'typeParameters', 'extends', 'body'],
DeclareModule: ['id', 'body'],
DeclareModuleExports: ['typeAnnotation'],
DeclareNamespace: ['id', 'body'],
DeclareOpaqueType: ['id', 'typeParameters', 'impltype', 'supertype'],
DeclareTypeAlias: ['id', 'typeParameters', 'right'],
DeclareVariable: ['id'],
DoWhileStatement: ['body', 'test'],
EmptyStatement: [],
EmptyTypeAnnotation: [],
EnumBigIntBody: ['members'],
EnumBigIntMember: ['id', 'init'],
EnumBooleanBody: ['members'],
EnumBooleanMember: ['id', 'init'],
EnumDeclaration: ['id', 'body'],
EnumDefaultedMember: ['id'],
EnumNumberBody: ['members'],
EnumNumberMember: ['id', 'init'],
EnumStringBody: ['members'],
EnumStringMember: ['id', 'init'],
EnumSymbolBody: ['members'],
ExistsTypeAnnotation: [],
ExportAllDeclaration: ['exported', 'source'],
ExportDefaultDeclaration: ['declaration'],
ExportNamedDeclaration: ['declaration', 'specifiers', 'source'],
ExportSpecifier: ['exported', 'local'],
ExpressionStatement: ['expression'],
ForInStatement: ['left', 'right', 'body'],
ForOfStatement: ['left', 'right', 'body'],
ForStatement: ['init', 'test', 'update', 'body'],
FunctionDeclaration: ['id', 'params', 'body', 'typeParameters', 'returnType', 'predicate'],
FunctionExpression: ['id', 'params', 'body', 'typeParameters', 'returnType', 'predicate'],
FunctionTypeAnnotation: ['params', 'this', 'returnType', 'rest', 'typeParameters'],
FunctionTypeParam: ['name', 'typeAnnotation'],
GenericTypeAnnotation: ['id', 'typeParameters'],
HookDeclaration: ['id', 'params', 'body', 'typeParameters', 'returnType'],
HookTypeAnnotation: ['params', 'returnType', 'rest', 'typeParameters'],
Identifier: ['typeAnnotation'],
IfStatement: ['test', 'consequent', 'alternate'],
ImportAttribute: ['key', 'value'],
ImportDeclaration: ['specifiers', 'source', 'assertions'],
ImportDefaultSpecifier: ['local'],
ImportExpression: ['source', 'attributes'],
ImportNamespaceSpecifier: ['local'],
ImportSpecifier: ['imported', 'local'],
IndexedAccessType: ['objectType', 'indexType'],
InferredPredicate: [],
InferTypeAnnotation: ['typeParameter'],
InterfaceDeclaration: ['id', 'typeParameters', 'extends', 'body'],
InterfaceExtends: ['id', 'typeParameters'],
InterfaceTypeAnnotation: ['extends', 'body'],
IntersectionTypeAnnotation: ['types'],
JSXAttribute: ['name', 'value'],
JSXClosingElement: ['name'],
JSXClosingFragment: [],
JSXElement: ['openingElement', 'children', 'closingElement'],
JSXEmptyExpression: [],
JSXExpressionContainer: ['expression'],
JSXFragment: ['openingFragment', 'children', 'closingFragment'],
JSXIdentifier: [],
JSXMemberExpression: ['object', 'property'],
JSXNamespacedName: ['namespace', 'name'],
JSXOpeningElement: ['name', 'attributes', 'typeArguments'],
JSXOpeningFragment: [],
JSXSpreadAttribute: ['argument'],
JSXSpreadChild: ['expression'],
JSXText: [],
KeyofTypeAnnotation: ['argument'],
LabeledStatement: ['label', 'body'],
LogicalExpression: ['left', 'right'],
MemberExpression: ['object', 'property'],
MetaProperty: ['meta', 'property'],
MethodDefinition: ['key', 'value'],
MixedTypeAnnotation: [],
NewExpression: ['callee', 'typeArguments', 'arguments'],
NullableTypeAnnotation: ['typeAnnotation'],
NullLiteralTypeAnnotation: [],
NumberLiteralTypeAnnotation: [],
NumberTypeAnnotation: [],
ObjectExpression: ['properties'],
ObjectPattern: ['properties', 'typeAnnotation'],
ObjectTypeAnnotation: ['properties', 'indexers', 'callProperties', 'internalSlots'],
ObjectTypeCallProperty: ['value'],
ObjectTypeIndexer: ['id', 'key', 'value', 'variance'],
ObjectTypeInternalSlot: ['id', 'value'],
ObjectTypeMappedTypeProperty: ['keyTparam', 'propType', 'sourceType', 'variance'],
ObjectTypeProperty: ['key', 'value', 'variance'],
ObjectTypeSpreadProperty: ['argument'],
OpaqueType: ['id', 'typeParameters', 'impltype', 'supertype'],
OptionalIndexedAccessType: ['objectType', 'indexType'],
PrivateIdentifier: [],
Program: ['body'],
Property: ['key', 'value'],
PropertyDefinition: ['key', 'value', 'variance', 'typeAnnotation'],
QualifiedTypeIdentifier: ['qualification', 'id'],
QualifiedTypeofIdentifier: ['qualification', 'id'],
RestElement: ['argument'],
ReturnStatement: ['argument'],
SequenceExpression: ['expressions'],
SpreadElement: ['argument'],
StringLiteralTypeAnnotation: [],
StringTypeAnnotation: [],
Super: [],
SwitchCase: ['test', 'consequent'],
SwitchStatement: ['discriminant', 'cases'],
SymbolTypeAnnotation: [],
TaggedTemplateExpression: ['tag', 'quasi'],
TemplateElement: [],
TemplateLiteral: ['quasis', 'expressions'],
ThisExpression: [],
ThisTypeAnnotation: [],
ThrowStatement: ['argument'],
TryStatement: ['block', 'handler', 'finalizer'],
TupleTypeAnnotation: ['types'],
TupleTypeLabeledElement: ['label', 'elementType', 'variance'],
TupleTypeSpreadElement: ['label', 'typeAnnotation'],
TypeAlias: ['id', 'typeParameters', 'right'],
TypeAnnotation: ['typeAnnotation'],
TypeCastExpression: ['expression', 'typeAnnotation'],
TypeofTypeAnnotation: ['argument', 'typeArguments'],
TypeOperator: ['typeAnnotation'],
TypeParameter: ['bound', 'variance', 'default'],
TypeParameterDeclaration: ['params'],
TypeParameterInstantiation: ['params'],
TypePredicate: ['parameterName', 'typeAnnotation'],
UnaryExpression: ['argument'],
UnionTypeAnnotation: ['types'],
UpdateExpression: ['argument'],
VariableDeclaration: ['declarations'],
VariableDeclarator: ['id', 'init'],
Variance: [],
VoidTypeAnnotation: [],
WhileStatement: ['test', 'body'],
WithStatement: ['object', 'body'],
YieldExpression: ['argument'],
OptionalMemberExpression: ['object', 'property'],
OptionalCallExpression: ['callee', 'typeArguments', 'arguments'],
Literal: []
};

View File

@@ -0,0 +1,15 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
'use strict';
export type VisitorKeys = $ReadOnly<{[string]: $ReadOnlyArray<string>}>;
declare module.exports: VisitorKeys;

View File

@@ -0,0 +1,730 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
* @generated
*/
/*
* !!! GENERATED FILE !!!
*
* Any manual changes to this file will be overwritten. To regenerate run `yarn build`.
*/
// lint directives to let us do some basic validation of generated files
/* eslint no-undef: 'error', no-unused-vars: ['error', {vars: "local"}], no-redeclare: 'error' */
/* global $NonMaybeType, Partial, $ReadOnly, $ReadOnlyArray, $FlowFixMe */
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.NODE_LIST_CHILD = exports.NODE_CHILD = exports.HERMES_AST_VISITOR_KEYS = void 0;
const NODE_CHILD = 'Node';
exports.NODE_CHILD = NODE_CHILD;
const NODE_LIST_CHILD = 'NodeList';
exports.NODE_LIST_CHILD = NODE_LIST_CHILD;
const HERMES_AST_VISITOR_KEYS = {
AnyTypeAnnotation: {},
ArrayExpression: {
elements: 'NodeList'
},
ArrayPattern: {
elements: 'NodeList',
typeAnnotation: 'Node'
},
ArrayTypeAnnotation: {
elementType: 'Node'
},
ArrowFunctionExpression: {
id: 'Node',
params: 'NodeList',
body: 'Node',
typeParameters: 'Node',
returnType: 'Node',
predicate: 'Node'
},
AsConstExpression: {
expression: 'Node'
},
AsExpression: {
expression: 'Node',
typeAnnotation: 'Node'
},
AssignmentExpression: {
left: 'Node',
right: 'Node'
},
AssignmentPattern: {
left: 'Node',
right: 'Node'
},
AwaitExpression: {
argument: 'Node'
},
BigIntLiteral: {},
BigIntLiteralTypeAnnotation: {},
BigIntTypeAnnotation: {},
BinaryExpression: {
left: 'Node',
right: 'Node'
},
BlockStatement: {
body: 'NodeList'
},
BooleanLiteral: {},
BooleanLiteralTypeAnnotation: {},
BooleanTypeAnnotation: {},
BreakStatement: {
label: 'Node'
},
CallExpression: {
callee: 'Node',
typeArguments: 'Node',
arguments: 'NodeList'
},
CatchClause: {
param: 'Node',
body: 'Node'
},
ChainExpression: {
expression: 'Node'
},
ClassBody: {
body: 'NodeList'
},
ClassDeclaration: {
id: 'Node',
typeParameters: 'Node',
superClass: 'Node',
superTypeParameters: 'Node',
implements: 'NodeList',
decorators: 'NodeList',
body: 'Node'
},
ClassExpression: {
id: 'Node',
typeParameters: 'Node',
superClass: 'Node',
superTypeParameters: 'Node',
implements: 'NodeList',
decorators: 'NodeList',
body: 'Node'
},
ClassImplements: {
id: 'Node',
typeParameters: 'Node'
},
ComponentDeclaration: {
id: 'Node',
params: 'NodeList',
body: 'Node',
typeParameters: 'Node',
rendersType: 'Node'
},
ComponentParameter: {
name: 'Node',
local: 'Node'
},
ComponentTypeAnnotation: {
params: 'NodeList',
rest: 'Node',
typeParameters: 'Node',
rendersType: 'Node'
},
ComponentTypeParameter: {
name: 'Node',
typeAnnotation: 'Node'
},
ConditionalExpression: {
test: 'Node',
alternate: 'Node',
consequent: 'Node'
},
ConditionalTypeAnnotation: {
checkType: 'Node',
extendsType: 'Node',
trueType: 'Node',
falseType: 'Node'
},
ContinueStatement: {
label: 'Node'
},
DebuggerStatement: {},
DeclareClass: {
id: 'Node',
typeParameters: 'Node',
extends: 'NodeList',
implements: 'NodeList',
mixins: 'NodeList',
body: 'Node'
},
DeclareComponent: {
id: 'Node',
params: 'NodeList',
rest: 'Node',
typeParameters: 'Node',
rendersType: 'Node'
},
DeclaredPredicate: {
value: 'Node'
},
DeclareEnum: {
id: 'Node',
body: 'Node'
},
DeclareExportAllDeclaration: {
source: 'Node'
},
DeclareExportDeclaration: {
declaration: 'Node',
specifiers: 'NodeList',
source: 'Node'
},
DeclareFunction: {
id: 'Node',
predicate: 'Node'
},
DeclareHook: {
id: 'Node'
},
DeclareInterface: {
id: 'Node',
typeParameters: 'Node',
extends: 'NodeList',
body: 'Node'
},
DeclareModule: {
id: 'Node',
body: 'Node'
},
DeclareModuleExports: {
typeAnnotation: 'Node'
},
DeclareNamespace: {
id: 'Node',
body: 'Node'
},
DeclareOpaqueType: {
id: 'Node',
typeParameters: 'Node',
impltype: 'Node',
supertype: 'Node'
},
DeclareTypeAlias: {
id: 'Node',
typeParameters: 'Node',
right: 'Node'
},
DeclareVariable: {
id: 'Node'
},
DoWhileStatement: {
body: 'Node',
test: 'Node'
},
EmptyStatement: {},
EmptyTypeAnnotation: {},
EnumBigIntBody: {
members: 'NodeList'
},
EnumBigIntMember: {
id: 'Node',
init: 'Node'
},
EnumBooleanBody: {
members: 'NodeList'
},
EnumBooleanMember: {
id: 'Node',
init: 'Node'
},
EnumDeclaration: {
id: 'Node',
body: 'Node'
},
EnumDefaultedMember: {
id: 'Node'
},
EnumNumberBody: {
members: 'NodeList'
},
EnumNumberMember: {
id: 'Node',
init: 'Node'
},
EnumStringBody: {
members: 'NodeList'
},
EnumStringMember: {
id: 'Node',
init: 'Node'
},
EnumSymbolBody: {
members: 'NodeList'
},
ExistsTypeAnnotation: {},
ExportAllDeclaration: {
exported: 'Node',
source: 'Node'
},
ExportDefaultDeclaration: {
declaration: 'Node'
},
ExportNamedDeclaration: {
declaration: 'Node',
specifiers: 'NodeList',
source: 'Node'
},
ExportSpecifier: {
exported: 'Node',
local: 'Node'
},
ExpressionStatement: {
expression: 'Node'
},
ForInStatement: {
left: 'Node',
right: 'Node',
body: 'Node'
},
ForOfStatement: {
left: 'Node',
right: 'Node',
body: 'Node'
},
ForStatement: {
init: 'Node',
test: 'Node',
update: 'Node',
body: 'Node'
},
FunctionDeclaration: {
id: 'Node',
params: 'NodeList',
body: 'Node',
typeParameters: 'Node',
returnType: 'Node',
predicate: 'Node'
},
FunctionExpression: {
id: 'Node',
params: 'NodeList',
body: 'Node',
typeParameters: 'Node',
returnType: 'Node',
predicate: 'Node'
},
FunctionTypeAnnotation: {
params: 'NodeList',
this: 'Node',
returnType: 'Node',
rest: 'Node',
typeParameters: 'Node'
},
FunctionTypeParam: {
name: 'Node',
typeAnnotation: 'Node'
},
GenericTypeAnnotation: {
id: 'Node',
typeParameters: 'Node'
},
HookDeclaration: {
id: 'Node',
params: 'NodeList',
body: 'Node',
typeParameters: 'Node',
returnType: 'Node'
},
HookTypeAnnotation: {
params: 'NodeList',
returnType: 'Node',
rest: 'Node',
typeParameters: 'Node'
},
Identifier: {
typeAnnotation: 'Node'
},
IfStatement: {
test: 'Node',
consequent: 'Node',
alternate: 'Node'
},
ImportAttribute: {
key: 'Node',
value: 'Node'
},
ImportDeclaration: {
specifiers: 'NodeList',
source: 'Node',
assertions: 'NodeList'
},
ImportDefaultSpecifier: {
local: 'Node'
},
ImportExpression: {
source: 'Node',
attributes: 'Node'
},
ImportNamespaceSpecifier: {
local: 'Node'
},
ImportSpecifier: {
imported: 'Node',
local: 'Node'
},
IndexedAccessType: {
objectType: 'Node',
indexType: 'Node'
},
InferredPredicate: {},
InferTypeAnnotation: {
typeParameter: 'Node'
},
InterfaceDeclaration: {
id: 'Node',
typeParameters: 'Node',
extends: 'NodeList',
body: 'Node'
},
InterfaceExtends: {
id: 'Node',
typeParameters: 'Node'
},
InterfaceTypeAnnotation: {
extends: 'NodeList',
body: 'Node'
},
IntersectionTypeAnnotation: {
types: 'NodeList'
},
JSXAttribute: {
name: 'Node',
value: 'Node'
},
JSXClosingElement: {
name: 'Node'
},
JSXClosingFragment: {},
JSXElement: {
openingElement: 'Node',
children: 'NodeList',
closingElement: 'Node'
},
JSXEmptyExpression: {},
JSXExpressionContainer: {
expression: 'Node'
},
JSXFragment: {
openingFragment: 'Node',
children: 'NodeList',
closingFragment: 'Node'
},
JSXIdentifier: {},
JSXMemberExpression: {
object: 'Node',
property: 'Node'
},
JSXNamespacedName: {
namespace: 'Node',
name: 'Node'
},
JSXOpeningElement: {
name: 'Node',
attributes: 'NodeList',
typeArguments: 'Node'
},
JSXOpeningFragment: {},
JSXSpreadAttribute: {
argument: 'Node'
},
JSXSpreadChild: {
expression: 'Node'
},
JSXText: {},
KeyofTypeAnnotation: {
argument: 'Node'
},
LabeledStatement: {
label: 'Node',
body: 'Node'
},
LogicalExpression: {
left: 'Node',
right: 'Node'
},
MemberExpression: {
object: 'Node',
property: 'Node'
},
MetaProperty: {
meta: 'Node',
property: 'Node'
},
MethodDefinition: {
key: 'Node',
value: 'Node'
},
MixedTypeAnnotation: {},
NewExpression: {
callee: 'Node',
typeArguments: 'Node',
arguments: 'NodeList'
},
NullableTypeAnnotation: {
typeAnnotation: 'Node'
},
NullLiteral: {},
NullLiteralTypeAnnotation: {},
NumberLiteralTypeAnnotation: {},
NumberTypeAnnotation: {},
NumericLiteral: {},
ObjectExpression: {
properties: 'NodeList'
},
ObjectPattern: {
properties: 'NodeList',
typeAnnotation: 'Node'
},
ObjectTypeAnnotation: {
properties: 'NodeList',
indexers: 'NodeList',
callProperties: 'NodeList',
internalSlots: 'NodeList'
},
ObjectTypeCallProperty: {
value: 'Node'
},
ObjectTypeIndexer: {
id: 'Node',
key: 'Node',
value: 'Node',
variance: 'Node'
},
ObjectTypeInternalSlot: {
id: 'Node',
value: 'Node'
},
ObjectTypeMappedTypeProperty: {
keyTparam: 'Node',
propType: 'Node',
sourceType: 'Node',
variance: 'Node'
},
ObjectTypeProperty: {
key: 'Node',
value: 'Node',
variance: 'Node'
},
ObjectTypeSpreadProperty: {
argument: 'Node'
},
OpaqueType: {
id: 'Node',
typeParameters: 'Node',
impltype: 'Node',
supertype: 'Node'
},
OptionalIndexedAccessType: {
objectType: 'Node',
indexType: 'Node'
},
PrivateIdentifier: {},
Program: {
body: 'NodeList'
},
Property: {
key: 'Node',
value: 'Node'
},
PropertyDefinition: {
key: 'Node',
value: 'Node',
variance: 'Node',
typeAnnotation: 'Node'
},
QualifiedTypeIdentifier: {
qualification: 'Node',
id: 'Node'
},
QualifiedTypeofIdentifier: {
qualification: 'Node',
id: 'Node'
},
RegExpLiteral: {},
RestElement: {
argument: 'Node'
},
ReturnStatement: {
argument: 'Node'
},
SequenceExpression: {
expressions: 'NodeList'
},
SpreadElement: {
argument: 'Node'
},
StringLiteral: {},
StringLiteralTypeAnnotation: {},
StringTypeAnnotation: {},
Super: {},
SwitchCase: {
test: 'Node',
consequent: 'NodeList'
},
SwitchStatement: {
discriminant: 'Node',
cases: 'NodeList'
},
SymbolTypeAnnotation: {},
TaggedTemplateExpression: {
tag: 'Node',
quasi: 'Node'
},
TemplateElement: {},
TemplateLiteral: {
quasis: 'NodeList',
expressions: 'NodeList'
},
ThisExpression: {},
ThisTypeAnnotation: {},
ThrowStatement: {
argument: 'Node'
},
TryStatement: {
block: 'Node',
handler: 'Node',
finalizer: 'Node'
},
TupleTypeAnnotation: {
types: 'NodeList'
},
TupleTypeLabeledElement: {
label: 'Node',
elementType: 'Node',
variance: 'Node'
},
TupleTypeSpreadElement: {
label: 'Node',
typeAnnotation: 'Node'
},
TypeAlias: {
id: 'Node',
typeParameters: 'Node',
right: 'Node'
},
TypeAnnotation: {
typeAnnotation: 'Node'
},
TypeCastExpression: {
expression: 'Node',
typeAnnotation: 'Node'
},
TypeofTypeAnnotation: {
argument: 'Node',
typeArguments: 'Node'
},
TypeOperator: {
typeAnnotation: 'Node'
},
TypeParameter: {
bound: 'Node',
variance: 'Node',
default: 'Node'
},
TypeParameterDeclaration: {
params: 'NodeList'
},
TypeParameterInstantiation: {
params: 'NodeList'
},
TypePredicate: {
parameterName: 'Node',
typeAnnotation: 'Node'
},
UnaryExpression: {
argument: 'Node'
},
UnionTypeAnnotation: {
types: 'NodeList'
},
UpdateExpression: {
argument: 'Node'
},
VariableDeclaration: {
declarations: 'NodeList'
},
VariableDeclarator: {
init: 'Node',
id: 'Node'
},
Variance: {},
VoidTypeAnnotation: {},
WhileStatement: {
body: 'Node',
test: 'Node'
},
WithStatement: {
object: 'Node',
body: 'Node'
},
YieldExpression: {
argument: 'Node'
},
File: {
program: 'Node'
},
ObjectProperty: {
key: 'Node',
value: 'Node'
},
ObjectMethod: {
key: 'Node',
params: 'NodeList',
body: 'Node',
returnType: 'Node',
typeParameters: 'NodeList'
},
ClassMethod: {
key: 'Node',
params: 'NodeList',
body: 'Node',
returnType: 'Node',
typeParameters: 'NodeList'
},
Import: {},
ClassProperty: {
key: 'Node',
value: 'Node',
variance: 'Node',
typeAnnotation: 'Node'
},
ClassPrivateProperty: {
key: 'Node',
value: 'Node',
variance: 'Node',
typeAnnotation: 'Node'
},
PrivateName: {
id: 'Node'
},
OptionalCallExpression: {
callee: 'Node',
typeArguments: 'Node',
arguments: 'NodeList'
},
OptionalMemberExpression: {
object: 'Node',
property: 'Node'
},
ExportNamespaceSpecifier: {
exported: 'Node'
}
};
exports.HERMES_AST_VISITOR_KEYS = HERMES_AST_VISITOR_KEYS;

View File

@@ -0,0 +1,17 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
declare export var NODE_CHILD: 'Node';
declare export var NODE_LIST_CHILD: 'NodeList';
declare export var HERMES_AST_VISITOR_KEYS: $ReadOnly<{
[string]: $ReadOnly<{
[string]: 'Node' | 'NodeList',
}>,
}>;

View File

@@ -0,0 +1,112 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getModuleDocblock = getModuleDocblock;
exports.parseDocblockString = parseDocblockString;
const DIRECTIVE_REGEX = /^\s*@([a-zA-Z0-9_-]+)( +.+)?$/;
function parseDocblockString(docblock) {
const directiveLines = docblock.split('\n') // remove the leading " *" from each line
.map(line => line.trimStart().replace(/^\* ?/, '').trim()).filter(line => line.startsWith('@'));
const directives = // $FlowExpectedError[incompatible-type] - flow types this return as {...}
Object.create(null);
for (const line of directiveLines) {
var _match$;
const match = DIRECTIVE_REGEX.exec(line);
if (match == null) {
continue;
}
const name = match[1]; // explicitly use an empty string if there's no value
// this way the array length tracks how many instances of the directive there was
const value = ((_match$ = match[2]) != null ? _match$ : '').trim();
if (directives[name]) {
directives[name].push(value);
} else {
directives[name] = [value];
}
}
return directives;
}
function getModuleDocblock(hermesProgram) {
const docblockNode = (() => {
if (hermesProgram.type !== 'Program') {
return null;
} // $FlowExpectedError[incompatible-type] - escape out of the unsafe hermes types
const program = hermesProgram;
if (program.comments.length === 0) {
return null;
}
const firstComment = (() => {
const first = program.comments[0];
if (first.type === 'Block') {
return first;
}
if (program.comments.length === 1) {
return null;
} // ESLint will always strip out the shebang comment from the code before passing it to the parser
// https://github.com/eslint/eslint/blob/21d647904dc30f9484b22acdd9243a6d0ecfba38/lib/linter/linter.js#L779
// this means that we're forced to parse it as a line comment :(
// this hacks around it by selecting the second comment in this case
const second = program.comments[1];
if (first.type === 'Line' && first.range[0] === 0 && second.type === 'Block') {
return second;
}
return null;
})();
if (firstComment == null) {
return null;
}
/*
Handle cases like this where the comment isn't actually the first thing in the code:
```
const x = 1; /* docblock *./
```
*/
if (program.body.length > 0 && program.body[0].range[0] < firstComment.range[0]) {
return null;
}
return firstComment;
})();
if (docblockNode == null) {
return null;
}
return {
directives: parseDocblockString(docblockNode.value),
comment: docblockNode
};
}

View File

@@ -0,0 +1,118 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
'use strict';
import type {HermesNode} from './HermesAST';
import type {DocblockDirectives, Program} from 'hermes-estree';
const DIRECTIVE_REGEX = /^\s*@([a-zA-Z0-9_-]+)( +.+)?$/;
export function parseDocblockString(docblock: string): DocblockDirectives {
const directiveLines = docblock
.split('\n')
// remove the leading " *" from each line
.map(line => line.trimStart().replace(/^\* ?/, '').trim())
.filter(line => line.startsWith('@'));
const directives: {
[string]: Array<string>,
} =
// $FlowExpectedError[incompatible-type] - flow types this return as {...}
Object.create(null);
for (const line of directiveLines) {
const match = DIRECTIVE_REGEX.exec(line);
if (match == null) {
continue;
}
const name = match[1];
// explicitly use an empty string if there's no value
// this way the array length tracks how many instances of the directive there was
const value = (match[2] ?? '').trim();
if (directives[name]) {
directives[name].push(value);
} else {
directives[name] = [value];
}
}
return directives;
}
export function getModuleDocblock(
hermesProgram: HermesNode,
): Program['docblock'] {
const docblockNode = (() => {
if (hermesProgram.type !== 'Program') {
return null;
}
// $FlowExpectedError[incompatible-type] - escape out of the unsafe hermes types
const program: Program = hermesProgram;
if (program.comments.length === 0) {
return null;
}
const firstComment = (() => {
const first = program.comments[0];
if (first.type === 'Block') {
return first;
}
if (program.comments.length === 1) {
return null;
}
// ESLint will always strip out the shebang comment from the code before passing it to the parser
// https://github.com/eslint/eslint/blob/21d647904dc30f9484b22acdd9243a6d0ecfba38/lib/linter/linter.js#L779
// this means that we're forced to parse it as a line comment :(
// this hacks around it by selecting the second comment in this case
const second = program.comments[1];
if (
first.type === 'Line' &&
first.range[0] === 0 &&
second.type === 'Block'
) {
return second;
}
return null;
})();
if (firstComment == null) {
return null;
}
/*
Handle cases like this where the comment isn't actually the first thing in the code:
```
const x = 1; /* docblock *./
```
*/
if (
program.body.length > 0 &&
program.body[0].range[0] < firstComment.range[0]
) {
return null;
}
return firstComment;
})();
if (docblockNode == null) {
return null;
}
return {
directives: parseDocblockString(docblockNode.value),
comment: docblockNode,
};
}

View File

@@ -0,0 +1,146 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _exportNames = {
parse: true,
mutateESTreeASTForPrettier: true,
Transforms: true,
FlowVisitorKeys: true,
astArrayMutationHelpers: true,
astNodeMutationHelpers: true
};
exports.mutateESTreeASTForPrettier = exports.astNodeMutationHelpers = exports.astArrayMutationHelpers = exports.Transforms = void 0;
exports.parse = parse;
var HermesParser = _interopRequireWildcard(require("./HermesParser"));
var _HermesToESTreeAdapter = _interopRequireDefault(require("./HermesToESTreeAdapter"));
var _ESTreeVisitorKeys = _interopRequireDefault(require("./generated/ESTreeVisitorKeys"));
exports.FlowVisitorKeys = _ESTreeVisitorKeys.default;
var StripComponentSyntax = _interopRequireWildcard(require("./estree/StripComponentSyntax"));
var StripFlowTypesForBabel = _interopRequireWildcard(require("./estree/StripFlowTypesForBabel"));
var TransformESTreeToBabel = _interopRequireWildcard(require("./babel/TransformESTreeToBabel"));
var StripFlowTypes = _interopRequireWildcard(require("./estree/StripFlowTypes"));
var _ParserOptions = require("./ParserOptions");
Object.keys(_ParserOptions).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _ParserOptions[key]) return;
exports[key] = _ParserOptions[key];
});
var _SimpleTraverser = require("./traverse/SimpleTraverser");
Object.keys(_SimpleTraverser).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _SimpleTraverser[key]) return;
exports[key] = _SimpleTraverser[key];
});
var _SimpleTransform = require("./transform/SimpleTransform");
Object.keys(_SimpleTransform).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _SimpleTransform[key]) return;
exports[key] = _SimpleTransform[key];
});
var _getVisitorKeys = require("./traverse/getVisitorKeys");
Object.keys(_getVisitorKeys).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
if (key in exports && exports[key] === _getVisitorKeys[key]) return;
exports[key] = _getVisitorKeys[key];
});
var _astArrayMutationHelpers = _interopRequireWildcard(require("./transform/astArrayMutationHelpers"));
exports.astArrayMutationHelpers = _astArrayMutationHelpers;
var _astNodeMutationHelpers = _interopRequireWildcard(require("./transform/astNodeMutationHelpers"));
exports.astNodeMutationHelpers = _astNodeMutationHelpers;
var _mutateESTreeASTForPrettier = _interopRequireDefault(require("./utils/mutateESTreeASTForPrettier"));
exports.mutateESTreeASTForPrettier = _mutateESTreeASTForPrettier.default;
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
const DEFAULTS = {
flow: 'detect'
};
function getOptions(options = { ...DEFAULTS
}) {
// Default to detecting whether to parse Flow syntax by the presence
// of an pragma.
if (options.flow == null) {
options.flow = DEFAULTS.flow;
} else if (options.flow !== 'all' && options.flow !== 'detect') {
throw new Error('flow option must be "all" or "detect"');
}
if (options.sourceType === 'unambiguous') {
// Clear source type so that it will be detected from the contents of the file
delete options.sourceType;
} else if (options.sourceType != null && options.sourceType !== 'script' && options.sourceType !== 'module') {
throw new Error('sourceType option must be "script", "module", or "unambiguous" if set');
}
if (options.enableExperimentalComponentSyntax == null) {
options.enableExperimentalComponentSyntax = true; // Enable by default
}
options.tokens = options.tokens === true;
options.allowReturnOutsideFunction = options.allowReturnOutsideFunction === true;
return options;
}
// eslint-disable-next-line no-redeclare
function parse(code, opts) {
const options = getOptions(opts);
const ast = HermesParser.parse(code, options);
const estreeAdapter = new _HermesToESTreeAdapter.default(options, code);
const estreeAST = estreeAdapter.transform(ast);
if (options.babel !== true) {
return estreeAST;
}
const loweredESTreeAST = [StripComponentSyntax.transformProgram, StripFlowTypesForBabel.transformProgram].reduce((ast, transform) => transform(ast, options), estreeAST);
return TransformESTreeToBabel.transformProgram(loweredESTreeAST, options);
}
const Transforms = {
stripComponentSyntax: StripComponentSyntax.transformProgram,
stripFlowTypesForBabel: StripFlowTypesForBabel.transformProgram,
stripFlowTypes: StripFlowTypes.transformProgram
};
exports.Transforms = Transforms;

View File

@@ -0,0 +1,112 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
'use strict';
import type {Program as ESTreeProgram} from 'hermes-estree';
import type {ParserOptions} from './ParserOptions';
import type {BabelFile} from './babel/TransformESTreeToBabel';
import * as HermesParser from './HermesParser';
import HermesToESTreeAdapter from './HermesToESTreeAdapter';
import FlowVisitorKeys from './generated/ESTreeVisitorKeys';
import * as StripComponentSyntax from './estree/StripComponentSyntax';
import * as StripFlowTypesForBabel from './estree/StripFlowTypesForBabel';
import * as TransformESTreeToBabel from './babel/TransformESTreeToBabel';
import * as StripFlowTypes from './estree/StripFlowTypes';
const DEFAULTS = {
flow: 'detect',
};
function getOptions(options?: ParserOptions = {...DEFAULTS}) {
// Default to detecting whether to parse Flow syntax by the presence
// of an @flow pragma.
if (options.flow == null) {
options.flow = DEFAULTS.flow;
} else if (options.flow !== 'all' && options.flow !== 'detect') {
throw new Error('flow option must be "all" or "detect"');
}
if (options.sourceType === 'unambiguous') {
// Clear source type so that it will be detected from the contents of the file
delete options.sourceType;
} else if (
options.sourceType != null &&
options.sourceType !== 'script' &&
options.sourceType !== 'module'
) {
throw new Error(
'sourceType option must be "script", "module", or "unambiguous" if set',
);
}
if (options.enableExperimentalComponentSyntax == null) {
options.enableExperimentalComponentSyntax = true; // Enable by default
}
options.tokens = options.tokens === true;
options.allowReturnOutsideFunction =
options.allowReturnOutsideFunction === true;
return options;
}
declare function parse(
code: string,
opts: {...ParserOptions, babel: true},
): BabelFile;
// eslint-disable-next-line no-redeclare
declare function parse(
code: string,
opts?:
| {...ParserOptions, babel?: false | void}
| {...ParserOptions, babel: false},
): ESTreeProgram;
// eslint-disable-next-line no-redeclare
export function parse(
code: string,
opts?: ParserOptions,
): BabelFile | ESTreeProgram {
const options = getOptions(opts);
const ast = HermesParser.parse(code, options);
const estreeAdapter = new HermesToESTreeAdapter(options, code);
const estreeAST = estreeAdapter.transform(ast);
if (options.babel !== true) {
return estreeAST;
}
const loweredESTreeAST = [
StripComponentSyntax.transformProgram,
StripFlowTypesForBabel.transformProgram,
].reduce((ast, transform) => transform(ast, options), estreeAST);
return TransformESTreeToBabel.transformProgram(loweredESTreeAST, options);
}
export type {ParserOptions} from './ParserOptions';
export * from './ParserOptions';
export * from './traverse/SimpleTraverser';
export * from './transform/SimpleTransform';
export * from './traverse/getVisitorKeys';
export {FlowVisitorKeys};
export * as astArrayMutationHelpers from './transform/astArrayMutationHelpers';
export * as astNodeMutationHelpers from './transform/astNodeMutationHelpers';
export {default as mutateESTreeASTForPrettier} from './utils/mutateESTreeASTForPrettier';
const Transforms = {
stripComponentSyntax: StripComponentSyntax.transformProgram,
stripFlowTypesForBabel: StripFlowTypesForBabel.transformProgram,
stripFlowTypes: StripFlowTypes.transformProgram,
};
export {Transforms};

View File

@@ -0,0 +1,120 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.SimpleTransform = void 0;
var _SimpleTraverser = require("../traverse/SimpleTraverser");
var _astNodeMutationHelpers = require("./astNodeMutationHelpers");
function setParentPointer(node, parent) {
if (parent != null) {
// $FlowExpectedError[cannot-write]
node.parent = parent;
}
}
/**
* A simple class to recursively tranform AST trees.
*/
class SimpleTransform {
/**
* Transform the given AST tree.
* @param rootNode The root node to traverse.
* @param options The option object.
* @return The modified rootNode or a new node if the rootNode was transformed directly.
*/
transform(rootNode, options) {
let resultRootNode = rootNode;
_SimpleTraverser.SimpleTraverser.traverse(rootNode, {
enter: (node, parent) => {
// Ensure the parent pointers are correctly set before entering the node.
setParentPointer(node, parent);
const resultNode = options.transform(node);
if (resultNode !== node) {
let traversedResultNode = null;
if (resultNode != null) {
// Ensure the new node has the correct parent pointers before recursing again.
setParentPointer(resultNode, parent);
traversedResultNode = this.transform(resultNode, options);
}
if (parent == null) {
if (node !== rootNode) {
throw new Error('SimpleTransform infra error: Parent not set on non root node, this should not be possible');
}
resultRootNode = traversedResultNode;
} else if (traversedResultNode == null) {
(0, _astNodeMutationHelpers.removeNodeOnParent)(node, parent, options.visitorKeys);
} else {
(0, _astNodeMutationHelpers.replaceNodeOnParent)(node, parent, traversedResultNode, options.visitorKeys);
setParentPointer(traversedResultNode, parent);
}
throw _SimpleTraverser.SimpleTraverser.Skip;
}
},
leave(_node) {},
visitorKeys: options.visitorKeys
});
return resultRootNode;
}
/**
* Transform the given AST tree.
* @param node The root node to traverse.
* @param options The option object.
*/
static transform(node, options) {
return new SimpleTransform().transform(node, options);
}
static transformProgram(program, options) {
const result = SimpleTransform.transform(program, options);
if ((result == null ? void 0 : result.type) === 'Program') {
return result;
}
throw new Error('SimpleTransform.transformProgram: Expected program node.');
}
/**
* Return a new AST node with the given properties overrided if needed.
*
* This function takes care to only create new nodes when needed. Referential equality of nodes
* is important as its used to know if a node should be re-traversed.
*
* @param node The base AST node.
* @param overrideProps New properties to apply to the node.
* @return Either the orginal node if the properties matched the existing node or a new node with
* the new properties.
*/
static nodeWith(node, overrideProps, visitorKeys) {
return (0, _astNodeMutationHelpers.nodeWith)(node, overrideProps, visitorKeys);
}
}
exports.SimpleTransform = SimpleTransform;

View File

@@ -0,0 +1,143 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
'use strict';
import type {VisitorKeysType} from '../traverse/getVisitorKeys';
import type {ESNode, Program} from 'hermes-estree';
import {SimpleTraverser} from '../traverse/SimpleTraverser';
import {
nodeWith,
removeNodeOnParent,
replaceNodeOnParent,
} from './astNodeMutationHelpers';
/**
* Transform callback
* @param node The node we are visiting
* @returns
* - return input node, signals no changes were made will continue to the next node.
* - return new node, the old node will be replaced in the AST. The new node and its
* children are then traversed.
* - return null, signals the node should be deleted from the AST.
*/
export type TransformCallback = (node: ESNode) => ESNode | null;
export type TransformOptions = $ReadOnly<{
/** The callback function which is called on entering each node. */
transform: TransformCallback,
/** The set of visitor keys to use for traversal. Defaults to the Flow ESTree visitor keys */
visitorKeys?: ?VisitorKeysType,
}>;
function setParentPointer(node: ESNode, parent: ?ESNode): void {
if (parent != null) {
// $FlowExpectedError[cannot-write]
node.parent = parent;
}
}
/**
* A simple class to recursively tranform AST trees.
*/
export class SimpleTransform {
/**
* Transform the given AST tree.
* @param rootNode The root node to traverse.
* @param options The option object.
* @return The modified rootNode or a new node if the rootNode was transformed directly.
*/
transform(rootNode: ESNode, options: TransformOptions): ESNode | null {
let resultRootNode: ESNode | null = rootNode;
SimpleTraverser.traverse(rootNode, {
enter: (node: ESNode, parent: ?ESNode) => {
// Ensure the parent pointers are correctly set before entering the node.
setParentPointer(node, parent);
const resultNode = options.transform(node);
if (resultNode !== node) {
let traversedResultNode = null;
if (resultNode != null) {
// Ensure the new node has the correct parent pointers before recursing again.
setParentPointer(resultNode, parent);
traversedResultNode = this.transform(resultNode, options);
}
if (parent == null) {
if (node !== rootNode) {
throw new Error(
'SimpleTransform infra error: Parent not set on non root node, this should not be possible',
);
}
resultRootNode = traversedResultNode;
} else if (traversedResultNode == null) {
removeNodeOnParent(node, parent, options.visitorKeys);
} else {
replaceNodeOnParent(
node,
parent,
traversedResultNode,
options.visitorKeys,
);
setParentPointer(traversedResultNode, parent);
}
throw SimpleTraverser.Skip;
}
},
leave(_node: ESNode) {},
visitorKeys: options.visitorKeys,
});
return resultRootNode;
}
/**
* Transform the given AST tree.
* @param node The root node to traverse.
* @param options The option object.
*/
static transform(node: ESNode, options: TransformOptions): ESNode | null {
return new SimpleTransform().transform(node, options);
}
static transformProgram(
program: Program,
options: TransformOptions,
): Program {
const result = SimpleTransform.transform(program, options);
if (result?.type === 'Program') {
return result;
}
throw new Error('SimpleTransform.transformProgram: Expected program node.');
}
/**
* Return a new AST node with the given properties overrided if needed.
*
* This function takes care to only create new nodes when needed. Referential equality of nodes
* is important as its used to know if a node should be re-traversed.
*
* @param node The base AST node.
* @param overrideProps New properties to apply to the node.
* @return Either the orginal node if the properties matched the existing node or a new node with
* the new properties.
*/
static nodeWith<T: ESNode>(
node: T,
overrideProps: Partial<T>,
visitorKeys?: VisitorKeysType,
): T {
return nodeWith<T>(node, overrideProps, visitorKeys);
}
}

View File

@@ -0,0 +1,62 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.arrayIsEqual = arrayIsEqual;
exports.insertInArray = insertInArray;
exports.removeFromArray = removeFromArray;
exports.replaceInArray = replaceInArray;
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
function assertArrayBounds(array, index) {
if (index < 0 || index >= array.length) {
throw new Error(`Invalid Mutation: Tried to mutate an elements array with an out of bounds index. Index: ${index}, Array Size: ${array.length}`);
}
}
function arrayIsEqual(a1, a2) {
if (a1 === a2) {
return true;
}
if (a1.length !== a2.length) {
return false;
}
for (let i = 0; i < a1.length; i++) {
if (a1[i] !== a2[i]) {
return false;
}
}
return true;
}
function insertInArray(array, index, elements) {
if (index === array.length) {
// Support the insert at end of array case.
return array.concat(elements);
}
assertArrayBounds(array, index);
return array.slice(0, index).concat(elements).concat(array.slice(index));
}
function removeFromArray(array, index) {
assertArrayBounds(array, index);
return [...array.slice(0, index), ...array.slice(index + 1)];
}
function replaceInArray(array, index, elements) {
assertArrayBounds(array, index);
return array.slice(0, index).concat(elements).concat(array.slice(index + 1));
}

View File

@@ -0,0 +1,71 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
function assertArrayBounds<T>(array: $ReadOnlyArray<T>, index: number): void {
if (index < 0 || index >= array.length) {
throw new Error(
`Invalid Mutation: Tried to mutate an elements array with an out of bounds index. Index: ${index}, Array Size: ${array.length}`,
);
}
}
export function arrayIsEqual(
a1: $ReadOnlyArray<mixed>,
a2: $ReadOnlyArray<mixed>,
): boolean {
if (a1 === a2) {
return true;
}
if (a1.length !== a2.length) {
return false;
}
for (let i = 0; i < a1.length; i++) {
if (a1[i] !== a2[i]) {
return false;
}
}
return true;
}
export function insertInArray<T>(
array: $ReadOnlyArray<T>,
index: number,
elements: $ReadOnlyArray<T>,
): Array<T> {
if (index === array.length) {
// Support the insert at end of array case.
return array.concat(elements);
}
assertArrayBounds(array, index);
return array.slice(0, index).concat(elements).concat(array.slice(index));
}
export function removeFromArray<T>(
array: $ReadOnlyArray<T>,
index: number,
): Array<T> {
assertArrayBounds(array, index);
return [...array.slice(0, index), ...array.slice(index + 1)];
}
export function replaceInArray<T>(
array: $ReadOnlyArray<T>,
index: number,
elements: $ReadOnlyArray<T>,
): Array<T> {
assertArrayBounds(array, index);
return array
.slice(0, index)
.concat(elements)
.concat(array.slice(index + 1));
}

View File

@@ -0,0 +1,193 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.deepCloneNode = deepCloneNode;
exports.nodeWith = nodeWith;
exports.removeNodeOnParent = removeNodeOnParent;
exports.replaceNodeOnParent = replaceNodeOnParent;
exports.setParentPointersInDirectChildren = setParentPointersInDirectChildren;
exports.shallowCloneNode = shallowCloneNode;
exports.updateAllParentPointers = updateAllParentPointers;
var _astArrayMutationHelpers = require("./astArrayMutationHelpers");
var _getVisitorKeys = require("../traverse/getVisitorKeys");
var _SimpleTraverser = require("../traverse/SimpleTraverser");
function getParentKey(target, parent, visitorKeys) {
if (parent == null) {
throw new Error(`Expected parent node to be set on "${target.type}"`);
}
for (const key of (0, _getVisitorKeys.getVisitorKeys)(parent, visitorKeys)) {
if ((0, _getVisitorKeys.isNode)( // $FlowExpectedError[prop-missing]
parent[key])) {
if (parent[key] === target) {
return {
type: 'single',
node: parent,
key
};
}
} else if (Array.isArray(parent[key])) {
for (let i = 0; i < parent[key].length; i += 1) {
const current = parent[key][i];
if (current === target) {
return {
type: 'array',
node: parent,
key,
targetIndex: i
};
}
}
}
} // this shouldn't happen ever
throw new Error(`Expected to find the ${target.type} as a direct child of the ${parent.type}.`);
}
/**
* Replace a node with a new node within an AST (via the parent pointer).
*/
function replaceNodeOnParent(originalNode, originalNodeParent, nodeToReplaceWith, visitorKeys) {
const replacementParent = getParentKey(originalNode, originalNodeParent, visitorKeys);
const parent = replacementParent.node;
if (replacementParent.type === 'array') {
// $FlowExpectedError[prop-missing]
parent[replacementParent.key] = (0, _astArrayMutationHelpers.replaceInArray)( // $FlowExpectedError[prop-missing]
parent[replacementParent.key], replacementParent.targetIndex, [nodeToReplaceWith]);
} else {
// $FlowExpectedError[prop-missing]
parent[replacementParent.key] = nodeToReplaceWith;
}
}
/**
* Remove a node from the AST its connected to (via the parent pointer).
*/
function removeNodeOnParent(originalNode, originalNodeParent, visitorKeys) {
const replacementParent = getParentKey(originalNode, originalNodeParent, visitorKeys);
const parent = replacementParent.node;
if (replacementParent.type === 'array') {
// $FlowExpectedError[prop-missing]
parent[replacementParent.key] = (0, _astArrayMutationHelpers.removeFromArray)( // $FlowExpectedError[prop-missing]
parent[replacementParent.key], replacementParent.targetIndex);
} else {
// $FlowExpectedError[prop-missing]
parent[replacementParent.key] = null;
}
}
/**
* Corrects the parent pointers in direct children of the given node.
*/
function setParentPointersInDirectChildren(node, visitorKeys) {
for (const key of (0, _getVisitorKeys.getVisitorKeys)(node, visitorKeys)) {
if ((0, _getVisitorKeys.isNode)(node[key])) {
node[key].parent = node;
} else if (Array.isArray(node[key])) {
for (const child of node[key]) {
child.parent = node;
}
}
}
}
/**
* Traverses the entire subtree to ensure the parent pointers are set correctly.
*/
function updateAllParentPointers(node, visitorKeys) {
_SimpleTraverser.SimpleTraverser.traverse(node, {
enter(node, parent) {
// $FlowExpectedError[cannot-write]
node.parent = parent;
},
leave() {},
visitorKeys
});
}
/**
* Clone node and add new props.
*
* This will only create a new object if the overrides actually result in a change.
*/
function nodeWith(node, overrideProps, visitorKeys) {
// Check if this will actually result in a change, maintaining referential equality is important.
const willBeUnchanged = Object.entries(overrideProps).every(([key, value]) => {
const node_ = node;
if (Array.isArray(value)) {
return Array.isArray(node_[key]) ? (0, _astArrayMutationHelpers.arrayIsEqual)(node_[key], value) : false;
}
return node_[key] === value;
});
if (willBeUnchanged) {
return node;
} // Create new node.
// $FlowExpectedError[cannot-spread-interface]
const newNode = { ...node,
...overrideProps
}; // Ensure parent pointers are correctly set within this nodes children.
setParentPointersInDirectChildren(newNode, visitorKeys);
return newNode;
}
/**
* Shallow clones node, providing a new reference for an existing node.
*/
function shallowCloneNode(node, visitorKeys) {
// $FlowExpectedError[cannot-spread-interface]
const newNode = { ...node
}; // Ensure parent pointers are correctly set within this nodes children.
setParentPointersInDirectChildren(newNode, visitorKeys);
return newNode;
}
/**
* Deeply clones node and its entire tree.
*/
function deepCloneNode(node, visitorKeys) {
const clone = JSON.parse(JSON.stringify(node, (key, value) => {
// null out parent pointers
if (key === 'parent') {
return undefined;
}
return value;
}));
updateAllParentPointers(clone, visitorKeys);
return clone;
}

View File

@@ -0,0 +1,236 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
'use strict';
import type {ESNode} from 'hermes-estree';
import type {VisitorKeysType} from '../traverse/getVisitorKeys';
import {
arrayIsEqual,
removeFromArray,
replaceInArray,
} from './astArrayMutationHelpers';
import {getVisitorKeys, isNode} from '../traverse/getVisitorKeys';
import {SimpleTraverser} from '../traverse/SimpleTraverser';
function getParentKey(
target: ESNode,
parent: ESNode,
visitorKeys?: ?VisitorKeysType,
): $ReadOnly<
| {
type: 'single',
node: ESNode,
key: string,
}
| {
type: 'array',
node: ESNode,
key: string,
targetIndex: number,
},
> {
if (parent == null) {
throw new Error(`Expected parent node to be set on "${target.type}"`);
}
for (const key of getVisitorKeys(parent, visitorKeys)) {
if (
isNode(
// $FlowExpectedError[prop-missing]
parent[key],
)
) {
if (parent[key] === target) {
return {type: 'single', node: parent, key};
}
} else if (Array.isArray(parent[key])) {
for (let i = 0; i < parent[key].length; i += 1) {
const current = parent[key][i];
if (current === target) {
return {type: 'array', node: parent, key, targetIndex: i};
}
}
}
}
// this shouldn't happen ever
throw new Error(
`Expected to find the ${target.type} as a direct child of the ${parent.type}.`,
);
}
/**
* Replace a node with a new node within an AST (via the parent pointer).
*/
export function replaceNodeOnParent(
originalNode: ESNode,
originalNodeParent: ESNode,
nodeToReplaceWith: ESNode,
visitorKeys?: ?VisitorKeysType,
): void {
const replacementParent = getParentKey(
originalNode,
originalNodeParent,
visitorKeys,
);
const parent = replacementParent.node;
if (replacementParent.type === 'array') {
// $FlowExpectedError[prop-missing]
parent[replacementParent.key] = replaceInArray(
// $FlowExpectedError[prop-missing]
parent[replacementParent.key],
replacementParent.targetIndex,
[nodeToReplaceWith],
);
} else {
// $FlowExpectedError[prop-missing]
parent[replacementParent.key] = nodeToReplaceWith;
}
}
/**
* Remove a node from the AST its connected to (via the parent pointer).
*/
export function removeNodeOnParent(
originalNode: ESNode,
originalNodeParent: ESNode,
visitorKeys?: ?VisitorKeysType,
): void {
const replacementParent = getParentKey(
originalNode,
originalNodeParent,
visitorKeys,
);
const parent = replacementParent.node;
if (replacementParent.type === 'array') {
// $FlowExpectedError[prop-missing]
parent[replacementParent.key] = removeFromArray(
// $FlowExpectedError[prop-missing]
parent[replacementParent.key],
replacementParent.targetIndex,
);
} else {
// $FlowExpectedError[prop-missing]
parent[replacementParent.key] = null;
}
}
/**
* Corrects the parent pointers in direct children of the given node.
*/
export function setParentPointersInDirectChildren(
node: ESNode,
visitorKeys?: ?VisitorKeysType,
): void {
for (const key: $FlowFixMe of getVisitorKeys(node, visitorKeys)) {
if (isNode(node[key])) {
node[key].parent = node;
} else if (Array.isArray(node[key])) {
for (const child of node[key]) {
child.parent = node;
}
}
}
}
/**
* Traverses the entire subtree to ensure the parent pointers are set correctly.
*/
export function updateAllParentPointers(
node: ESNode,
visitorKeys?: ?VisitorKeysType,
) {
SimpleTraverser.traverse(node, {
enter(node, parent) {
// $FlowExpectedError[cannot-write]
node.parent = parent;
},
leave() {},
visitorKeys,
});
}
/**
* Clone node and add new props.
*
* This will only create a new object if the overrides actually result in a change.
*/
export function nodeWith<T: ESNode>(
node: T,
overrideProps: Partial<T>,
visitorKeys?: ?VisitorKeysType,
): T {
// Check if this will actually result in a change, maintaining referential equality is important.
const willBeUnchanged = Object.entries(overrideProps).every(
([key, value]) => {
const node_: $FlowFixMe = node;
if (Array.isArray(value)) {
return Array.isArray(node_[key])
? arrayIsEqual(node_[key], value)
: false;
}
return node_[key] === value;
},
);
if (willBeUnchanged) {
return node;
}
// Create new node.
// $FlowExpectedError[cannot-spread-interface]
const newNode: T = {
...node,
...overrideProps,
};
// Ensure parent pointers are correctly set within this nodes children.
setParentPointersInDirectChildren(newNode, visitorKeys);
return newNode;
}
/**
* Shallow clones node, providing a new reference for an existing node.
*/
export function shallowCloneNode<T: ESNode>(
node: T,
visitorKeys?: ?VisitorKeysType,
): T {
// $FlowExpectedError[cannot-spread-interface]
const newNode: T = {...node};
// Ensure parent pointers are correctly set within this nodes children.
setParentPointersInDirectChildren(newNode, visitorKeys);
return newNode;
}
/**
* Deeply clones node and its entire tree.
*/
export function deepCloneNode<T: ESNode>(
node: T,
visitorKeys?: ?VisitorKeysType,
): T {
const clone: T = JSON.parse(
JSON.stringify(node, (key, value) => {
// null out parent pointers
if (key === 'parent') {
return undefined;
}
return value;
}),
);
updateAllParentPointers(clone, visitorKeys);
return clone;
}

View File

@@ -0,0 +1,137 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.SimpleTraverserSkip = exports.SimpleTraverserBreak = exports.SimpleTraverser = void 0;
var _getVisitorKeys = require("./getVisitorKeys");
/**
* Can be thrown within the traversal "enter" function to prevent the traverser
* from traversing the node any further, essentially culling the remainder of the
* AST branch
*/
const SimpleTraverserSkip = new Error();
/**
* Can be thrown at any point during the traversal to immediately stop traversal
* entirely.
*/
exports.SimpleTraverserSkip = SimpleTraverserSkip;
const SimpleTraverserBreak = new Error();
/**
* A very simple traverser class to traverse AST trees.
*/
exports.SimpleTraverserBreak = SimpleTraverserBreak;
class SimpleTraverser {
/**
* Traverse the given AST tree.
* @param node The root node to traverse.
* @param options The option object.
*/
traverse(node, options) {
try {
this._traverse(node, null, options);
} catch (ex) {
if (ex === SimpleTraverserBreak) {
return;
}
throw ex;
}
}
/**
* Traverse the given AST tree recursively.
* @param node The current node.
* @param parent The parent node.
* @private
*/
_traverse(node, parent, options) {
if (!(0, _getVisitorKeys.isNode)(node)) {
return;
}
try {
options.enter(node, parent);
} catch (ex) {
if (ex === SimpleTraverserSkip) {
return;
}
this._setErrorContext(ex, node);
throw ex;
}
const keys = (0, _getVisitorKeys.getVisitorKeys)(node, options.visitorKeys);
for (const key of keys) {
const child = node[key];
if (Array.isArray(child)) {
for (let j = 0; j < child.length; ++j) {
this._traverse(child[j], node, options);
}
} else {
this._traverse(child, node, options);
}
}
try {
options.leave(node, parent);
} catch (ex) {
if (ex === SimpleTraverserSkip) {
return;
}
this._setErrorContext(ex, node);
throw ex;
}
}
/**
* Set useful contextual information onto the error object.
* @param ex The error object.
* @param node The current node.
* @private
*/
_setErrorContext(ex, node) {
// $FlowFixMe[prop-missing]
ex.currentNode = {
type: node.type,
range: node.range,
loc: node.loc
};
}
/**
* Traverse the given AST tree.
* @param node The root node to traverse.
* @param options The option object.
*/
static traverse(node, options) {
new SimpleTraverser().traverse(node, options);
}
}
exports.SimpleTraverser = SimpleTraverser;
SimpleTraverser.Break = SimpleTraverserBreak;
SimpleTraverser.Skip = SimpleTraverserSkip;

View File

@@ -0,0 +1,133 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
'use strict';
import type {VisitorKeysType} from './getVisitorKeys';
import type {ESNode} from 'hermes-estree';
import {getVisitorKeys, isNode} from './getVisitorKeys';
export type TraverserCallback = (node: ESNode, parent: ?ESNode) => void;
export type TraverserOptions = $ReadOnly<{
/** The callback function which is called on entering each node. */
enter: TraverserCallback,
/** The callback function which is called on leaving each node. */
leave: TraverserCallback,
/** The set of visitor keys to use for traversal. Defaults to the Flow ESTree visitor keys */
visitorKeys?: ?VisitorKeysType,
}>;
/**
* Can be thrown within the traversal "enter" function to prevent the traverser
* from traversing the node any further, essentially culling the remainder of the
* AST branch
*/
export const SimpleTraverserSkip: Error = new Error();
/**
* Can be thrown at any point during the traversal to immediately stop traversal
* entirely.
*/
export const SimpleTraverserBreak: Error = new Error();
/**
* A very simple traverser class to traverse AST trees.
*/
export class SimpleTraverser {
static Break: Error = SimpleTraverserBreak;
static Skip: Error = SimpleTraverserSkip;
/**
* Traverse the given AST tree.
* @param node The root node to traverse.
* @param options The option object.
*/
traverse(node: ESNode, options: TraverserOptions): void {
try {
this._traverse(node, null, options);
} catch (ex) {
if (ex === SimpleTraverserBreak) {
return;
}
throw ex;
}
}
/**
* Traverse the given AST tree recursively.
* @param node The current node.
* @param parent The parent node.
* @private
*/
_traverse(node: ESNode, parent: ?ESNode, options: TraverserOptions): void {
if (!isNode(node)) {
return;
}
try {
options.enter(node, parent);
} catch (ex) {
if (ex === SimpleTraverserSkip) {
return;
}
this._setErrorContext(ex, node);
throw ex;
}
const keys = getVisitorKeys(node, options.visitorKeys);
for (const key of keys) {
const child: ESNode | $ReadOnlyArray<ESNode> = (node[
(key: $FlowFixMe)
]: $FlowFixMe);
if (Array.isArray(child)) {
for (let j = 0; j < child.length; ++j) {
this._traverse(child[j], node, options);
}
} else {
this._traverse(child, node, options);
}
}
try {
options.leave(node, parent);
} catch (ex) {
if (ex === SimpleTraverserSkip) {
return;
}
this._setErrorContext(ex, node);
throw ex;
}
}
/**
* Set useful contextual information onto the error object.
* @param ex The error object.
* @param node The current node.
* @private
*/
_setErrorContext(ex: Error, node: ESNode): void {
// $FlowFixMe[prop-missing]
ex.currentNode = {
type: node.type,
range: node.range,
loc: node.loc,
};
}
/**
* Traverse the given AST tree.
* @param node The root node to traverse.
* @param options The option object.
*/
static traverse(node: ESNode, options: TraverserOptions) {
new SimpleTraverser().traverse(node, options);
}
}

View File

@@ -0,0 +1,36 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.getVisitorKeys = getVisitorKeys;
exports.isNode = isNode;
var _ESTreeVisitorKeys = _interopRequireDefault(require("../generated/ESTreeVisitorKeys"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// $FlowFixMe[deprecated-type]
function isNode(thing) {
return typeof thing === 'object' && thing != null && typeof thing.type === 'string';
}
function getVisitorKeys(node, visitorKeys) {
const keys = (visitorKeys != null ? visitorKeys : _ESTreeVisitorKeys.default)[node.type];
if (keys == null) {
throw new Error(`No visitor keys found for node type "${node.type}".`);
} // $FlowExpectedError[prop-missing]
return keys;
}

View File

@@ -0,0 +1,37 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
'use strict';
import type {ESNode} from 'hermes-estree';
import type {VisitorKeys as VisitorKeysType} from '../generated/ESTreeVisitorKeys';
import FlowVisitorKeys from '../generated/ESTreeVisitorKeys';
// $FlowFixMe[deprecated-type]
export function isNode(thing: mixed): boolean %checks {
return (
typeof thing === 'object' && thing != null && typeof thing.type === 'string'
);
}
export type {VisitorKeysType};
export function getVisitorKeys<T: ESNode>(
node: T,
visitorKeys?: ?VisitorKeysType,
): $ReadOnlyArray<$Keys<T>> {
const keys = (visitorKeys ?? FlowVisitorKeys)[node.type];
if (keys == null) {
throw new Error(`No visitor keys found for node type "${node.type}".`);
}
// $FlowExpectedError[prop-missing]
return keys;
}

View File

@@ -0,0 +1,25 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.createSyntaxError = createSyntaxError;
function createSyntaxError(node, err) {
const syntaxError = new SyntaxError(err); // $FlowExpectedError[prop-missing]
syntaxError.loc = {
line: node.loc.start.line,
column: node.loc.start.column
};
return syntaxError;
}

View File

@@ -0,0 +1,24 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
'use strict';
import type {ESNode} from 'hermes-estree';
export function createSyntaxError(node: ESNode, err: string): SyntaxError {
const syntaxError = new SyntaxError(err);
// $FlowExpectedError[prop-missing]
syntaxError.loc = {
line: node.loc.start.line,
column: node.loc.start.column,
};
return syntaxError;
}

View File

@@ -0,0 +1,127 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = mutate;
var _SimpleTransform = require("../transform/SimpleTransform");
// https://github.com/prettier/prettier/blob/d962466a828f8ef51435e3e8840178d90b7ec6cd/src/language-js/parse/postprocess/index.js#L161-L182
function transformChainExpression(node, comments) {
if (comments != null) {
var _node$comments;
// $FlowExpectedError[prop-missing]
const joinedComments = comments.concat((_node$comments = node.comments) != null ? _node$comments : []); // $FlowExpectedError[prop-missing]
// $FlowFixMe[cannot-write]
node.comments = joinedComments;
}
switch (node.type) {
case 'CallExpression':
// $FlowExpectedError[cannot-spread-interface]
return { ...node,
type: 'OptionalCallExpression',
callee: transformChainExpression(node.callee)
};
case 'MemberExpression':
// $FlowExpectedError[cannot-spread-interface]
return { ...node,
type: 'OptionalMemberExpression',
object: transformChainExpression(node.object)
};
// No default
}
return node;
}
function mutate(rootNode, visitorKeys) {
// Since we don't return the result of `transform` we need to be careful not to replace the Program root node.
_SimpleTransform.SimpleTransform.transform(rootNode, {
transform(node) {
// prettier fully expects the parent pointers are NOT set and
// certain cases can crash due to prettier infinite-looping
// whilst naively traversing the parent property
// https://github.com/prettier/prettier/issues/11793
// Note: Only needed for prettier V2, this is supported in V3
if (node.parent) {
// $FlowExpectedError[cannot-write]
delete node.parent;
} // prettier currently relies on the AST being in the old-school, deprecated AST format for optional chaining
// so we have to apply their transform to our AST so it can actually format it.
// Note: Only needed for prettier V2, this is supported in V3
if (node.type === 'ChainExpression') {
// $FlowFixMe[prop-missing]
return transformChainExpression(node.expression, node == null ? void 0 : node.comments);
} // Prettier currently relies on comparing the `node` vs `node.value` start positions to know if an
// `ObjectTypeProperty` is a method or not (instead of using the `node.method` boolean). To correctly print
// the node when its not a method we need the start position to be different from the `node.value`s start
// position.
if (node.type === 'ObjectTypeProperty') {
if (node.method === false && node.kind === 'init' && node.range[0] === 1 && node.value.range[0] === 1) {
// $FlowExpectedError[cannot-write]
// $FlowExpectedError[cannot-spread-interface]
node.value = { ...node.value,
range: [2, node.value.range[1]]
};
}
return node;
} // Prettier currently relies on comparing the the start positions to know if the import/export specifier should have a
// rename (eg `Name` vs `Name as Name`) when the name is exactly the same
// So we need to ensure that the range is always the same to avoid the useless code printing
if (node.type === 'ImportSpecifier') {
if (node.local.name === node.imported.name) {
if (node.local.range == null) {
// for our TS-ast printing which has no locs
// $FlowExpectedError[cannot-write]
node.local.range = [0, 0];
} // $FlowExpectedError[cannot-write]
node.imported.range = [...node.local.range];
}
return node;
}
if (node.type === 'ExportSpecifier') {
if (node.local.name === node.exported.name) {
if (node.local.range == null) {
// for our TS-ast printing which has no locs
// $FlowExpectedError[cannot-write]
node.local.range = [0, 0];
} // $FlowExpectedError[cannot-write]
node.exported.range = [...node.local.range];
}
return node;
}
return node;
},
visitorKeys
});
}

View File

@@ -0,0 +1,130 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow strict
* @format
*/
'use strict';
import type {ESNode, Program, Comment} from 'hermes-estree';
import type {VisitorKeysType} from '../traverse/getVisitorKeys';
import {SimpleTransform} from '../transform/SimpleTransform';
// https://github.com/prettier/prettier/blob/d962466a828f8ef51435e3e8840178d90b7ec6cd/src/language-js/parse/postprocess/index.js#L161-L182
function transformChainExpression(
node: ESNode,
comments: ?$ReadOnlyArray<Comment>,
): ESNode {
if (comments != null) {
// $FlowExpectedError[prop-missing]
const joinedComments = comments.concat(node.comments ?? []);
// $FlowExpectedError[prop-missing]
// $FlowFixMe[cannot-write]
node.comments = joinedComments;
}
switch (node.type) {
case 'CallExpression':
// $FlowExpectedError[cannot-spread-interface]
return {
...node,
type: 'OptionalCallExpression',
callee: transformChainExpression(node.callee),
};
case 'MemberExpression':
// $FlowExpectedError[cannot-spread-interface]
return {
...node,
type: 'OptionalMemberExpression',
object: transformChainExpression(node.object),
};
// No default
}
return node;
}
export default function mutate(
rootNode: Program,
visitorKeys: ?VisitorKeysType,
): void {
// Since we don't return the result of `transform` we need to be careful not to replace the Program root node.
SimpleTransform.transform(rootNode, {
transform(node): ESNode | null {
// prettier fully expects the parent pointers are NOT set and
// certain cases can crash due to prettier infinite-looping
// whilst naively traversing the parent property
// https://github.com/prettier/prettier/issues/11793
// Note: Only needed for prettier V2, this is supported in V3
if (node.parent) {
// $FlowExpectedError[cannot-write]
delete node.parent;
}
// prettier currently relies on the AST being in the old-school, deprecated AST format for optional chaining
// so we have to apply their transform to our AST so it can actually format it.
// Note: Only needed for prettier V2, this is supported in V3
if (node.type === 'ChainExpression') {
// $FlowFixMe[prop-missing]
return transformChainExpression(node.expression, node?.comments);
}
// Prettier currently relies on comparing the `node` vs `node.value` start positions to know if an
// `ObjectTypeProperty` is a method or not (instead of using the `node.method` boolean). To correctly print
// the node when its not a method we need the start position to be different from the `node.value`s start
// position.
if (node.type === 'ObjectTypeProperty') {
if (
node.method === false &&
node.kind === 'init' &&
node.range[0] === 1 &&
node.value.range[0] === 1
) {
// $FlowExpectedError[cannot-write]
// $FlowExpectedError[cannot-spread-interface]
node.value = {
...node.value,
range: [2, node.value.range[1]],
};
}
return node;
}
// Prettier currently relies on comparing the the start positions to know if the import/export specifier should have a
// rename (eg `Name` vs `Name as Name`) when the name is exactly the same
// So we need to ensure that the range is always the same to avoid the useless code printing
if (node.type === 'ImportSpecifier') {
if (node.local.name === node.imported.name) {
if (node.local.range == null) {
// for our TS-ast printing which has no locs
// $FlowExpectedError[cannot-write]
node.local.range = [0, 0];
}
// $FlowExpectedError[cannot-write]
node.imported.range = [...node.local.range];
}
return node;
}
if (node.type === 'ExportSpecifier') {
if (node.local.name === node.exported.name) {
if (node.local.range == null) {
// for our TS-ast printing which has no locs
// $FlowExpectedError[cannot-write]
node.local.range = [0, 0];
}
// $FlowExpectedError[cannot-write]
node.exported.range = [...node.local.range];
}
return node;
}
return node;
},
visitorKeys,
});
}

View File

@@ -0,0 +1,25 @@
{
"name": "hermes-parser",
"version": "0.23.1",
"description": "A JavaScript parser built from the Hermes engine",
"main": "dist/index.js",
"license": "MIT",
"repository": {
"type": "git",
"url": "git@github.com:facebook/hermes.git"
},
"dependencies": {
"hermes-estree": "0.23.1"
},
"devDependencies": {
"@babel/parser": "7.7.4",
"@babel/generator": "7.7.4",
"@babel/types": "7.7.4",
"espree": "9.3.2"
},
"files": [
"dist",
"LICENCE",
"README.md"
]
}

View File

@@ -0,0 +1,152 @@
/**
* Helpers.
*/
var s = 1000;
var m = s * 60;
var h = m * 60;
var d = h * 24;
var y = d * 365.25;
/**
* Parse or format the given `val`.
*
* Options:
*
* - `long` verbose formatting [false]
*
* @param {String|Number} val
* @param {Object} [options]
* @throws {Error} throw an error if val is not a non-empty string or a number
* @return {String|Number}
* @api public
*/
module.exports = function(val, options) {
options = options || {};
var type = typeof val;
if (type === 'string' && val.length > 0) {
return parse(val);
} else if (type === 'number' && isNaN(val) === false) {
return options.long ? fmtLong(val) : fmtShort(val);
}
throw new Error(
'val is not a non-empty string or a valid number. val=' +
JSON.stringify(val)
);
};
/**
* Parse the given `str` and return milliseconds.
*
* @param {String} str
* @return {Number}
* @api private
*/
function parse(str) {
str = String(str);
if (str.length > 100) {
return;
}
var match = /^((?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|years?|yrs?|y)?$/i.exec(
str
);
if (!match) {
return;
}
var n = parseFloat(match[1]);
var type = (match[2] || 'ms').toLowerCase();
switch (type) {
case 'years':
case 'year':
case 'yrs':
case 'yr':
case 'y':
return n * y;
case 'days':
case 'day':
case 'd':
return n * d;
case 'hours':
case 'hour':
case 'hrs':
case 'hr':
case 'h':
return n * h;
case 'minutes':
case 'minute':
case 'mins':
case 'min':
case 'm':
return n * m;
case 'seconds':
case 'second':
case 'secs':
case 'sec':
case 's':
return n * s;
case 'milliseconds':
case 'millisecond':
case 'msecs':
case 'msec':
case 'ms':
return n;
default:
return undefined;
}
}
/**
* Short format for `ms`.
*
* @param {Number} ms
* @return {String}
* @api private
*/
function fmtShort(ms) {
if (ms >= d) {
return Math.round(ms / d) + 'd';
}
if (ms >= h) {
return Math.round(ms / h) + 'h';
}
if (ms >= m) {
return Math.round(ms / m) + 'm';
}
if (ms >= s) {
return Math.round(ms / s) + 's';
}
return ms + 'ms';
}
/**
* Long format for `ms`.
*
* @param {Number} ms
* @return {String}
* @api private
*/
function fmtLong(ms) {
return plural(ms, d, 'day') ||
plural(ms, h, 'hour') ||
plural(ms, m, 'minute') ||
plural(ms, s, 'second') ||
ms + ' ms';
}
/**
* Pluralization helper.
*/
function plural(ms, n, name) {
if (ms < n) {
return;
}
if (ms < n * 1.5) {
return Math.floor(ms / n) + ' ' + name;
}
return Math.ceil(ms / n) + ' ' + name + 's';
}

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2016 Zeit, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,37 @@
{
"name": "ms",
"version": "2.0.0",
"description": "Tiny milisecond conversion utility",
"repository": "zeit/ms",
"main": "./index",
"files": [
"index.js"
],
"scripts": {
"precommit": "lint-staged",
"lint": "eslint lib/* bin/*",
"test": "mocha tests.js"
},
"eslintConfig": {
"extends": "eslint:recommended",
"env": {
"node": true,
"es6": true
}
},
"lint-staged": {
"*.js": [
"npm run lint",
"prettier --single-quote --write",
"git add"
]
},
"license": "MIT",
"devDependencies": {
"eslint": "3.19.0",
"expect.js": "0.3.1",
"husky": "0.13.3",
"lint-staged": "3.4.1",
"mocha": "3.4.1"
}
}

View File

@@ -0,0 +1,51 @@
# ms
[![Build Status](https://travis-ci.org/zeit/ms.svg?branch=master)](https://travis-ci.org/zeit/ms)
[![Slack Channel](http://zeit-slackin.now.sh/badge.svg)](https://zeit.chat/)
Use this package to easily convert various time formats to milliseconds.
## Examples
```js
ms('2 days') // 172800000
ms('1d') // 86400000
ms('10h') // 36000000
ms('2.5 hrs') // 9000000
ms('2h') // 7200000
ms('1m') // 60000
ms('5s') // 5000
ms('1y') // 31557600000
ms('100') // 100
```
### Convert from milliseconds
```js
ms(60000) // "1m"
ms(2 * 60000) // "2m"
ms(ms('10 hours')) // "10h"
```
### Time format written-out
```js
ms(60000, { long: true }) // "1 minute"
ms(2 * 60000, { long: true }) // "2 minutes"
ms(ms('10 hours'), { long: true }) // "10 hours"
```
## Features
- Works both in [node](https://nodejs.org) and in the browser.
- If a number is supplied to `ms`, a string with a unit is returned.
- If a string that contains the number is supplied, it returns it as a number (e.g.: it returns `100` for `'100'`).
- If you pass a string with a number and a valid unit, the number of equivalent ms is returned.
## Caught a bug?
1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device
2. Link the package to the global module directory: `npm link`
3. Within the module you want to test your local development instance of ms, just link it to the dependencies: `npm link ms`. Instead of the default one from npm, node will now use your clone of ms!
As always, you can run the tests using: `npm test`

View File

@@ -0,0 +1,301 @@
# Change Log
## 0.5.6
* Fix for regression when people were using numbers as names in source maps. See
#236.
## 0.5.5
* Fix "regression" of unsupported, implementation behavior that half the world
happens to have come to depend on. See #235.
* Fix regression involving function hoisting in SpiderMonkey. See #233.
## 0.5.4
* Large performance improvements to source-map serialization. See #228 and #229.
## 0.5.3
* Do not include unnecessary distribution files. See
commit ef7006f8d1647e0a83fdc60f04f5a7ca54886f86.
## 0.5.2
* Include browser distributions of the library in package.json's `files`. See
issue #212.
## 0.5.1
* Fix latent bugs in IndexedSourceMapConsumer.prototype._parseMappings. See
ff05274becc9e6e1295ed60f3ea090d31d843379.
## 0.5.0
* Node 0.8 is no longer supported.
* Use webpack instead of dryice for bundling.
* Big speedups serializing source maps. See pull request #203.
* Fix a bug with `SourceMapConsumer.prototype.sourceContentFor` and sources that
explicitly start with the source root. See issue #199.
## 0.4.4
* Fix an issue where using a `SourceMapGenerator` after having created a
`SourceMapConsumer` from it via `SourceMapConsumer.fromSourceMap` failed. See
issue #191.
* Fix an issue with where `SourceMapGenerator` would mistakenly consider
different mappings as duplicates of each other and avoid generating them. See
issue #192.
## 0.4.3
* A very large number of performance improvements, particularly when parsing
source maps. Collectively about 75% of time shaved off of the source map
parsing benchmark!
* Fix a bug in `SourceMapConsumer.prototype.allGeneratedPositionsFor` and fuzzy
searching in the presence of a column option. See issue #177.
* Fix a bug with joining a source and its source root when the source is above
the root. See issue #182.
* Add the `SourceMapConsumer.prototype.hasContentsOfAllSources` method to
determine when all sources' contents are inlined into the source map. See
issue #190.
## 0.4.2
* Add an `.npmignore` file so that the benchmarks aren't pulled down by
dependent projects. Issue #169.
* Add an optional `column` argument to
`SourceMapConsumer.prototype.allGeneratedPositionsFor` and better handle lines
with no mappings. Issues #172 and #173.
## 0.4.1
* Fix accidentally defining a global variable. #170.
## 0.4.0
* The default direction for fuzzy searching was changed back to its original
direction. See #164.
* There is now a `bias` option you can supply to `SourceMapConsumer` to control
the fuzzy searching direction. See #167.
* About an 8% speed up in parsing source maps. See #159.
* Added a benchmark for parsing and generating source maps.
## 0.3.0
* Change the default direction that searching for positions fuzzes when there is
not an exact match. See #154.
* Support for environments using json2.js for JSON serialization. See #156.
## 0.2.0
* Support for consuming "indexed" source maps which do not have any remote
sections. See pull request #127. This introduces a minor backwards
incompatibility if you are monkey patching `SourceMapConsumer.prototype`
methods.
## 0.1.43
* Performance improvements for `SourceMapGenerator` and `SourceNode`. See issue
#148 for some discussion and issues #150, #151, and #152 for implementations.
## 0.1.42
* Fix an issue where `SourceNode`s from different versions of the source-map
library couldn't be used in conjunction with each other. See issue #142.
## 0.1.41
* Fix a bug with getting the source content of relative sources with a "./"
prefix. See issue #145 and [Bug 1090768](bugzil.la/1090768).
* Add the `SourceMapConsumer.prototype.computeColumnSpans` method to compute the
column span of each mapping.
* Add the `SourceMapConsumer.prototype.allGeneratedPositionsFor` method to find
all generated positions associated with a given original source and line.
## 0.1.40
* Performance improvements for parsing source maps in SourceMapConsumer.
## 0.1.39
* Fix a bug where setting a source's contents to null before any source content
had been set before threw a TypeError. See issue #131.
## 0.1.38
* Fix a bug where finding relative paths from an empty path were creating
absolute paths. See issue #129.
## 0.1.37
* Fix a bug where if the source root was an empty string, relative source paths
would turn into absolute source paths. Issue #124.
## 0.1.36
* Allow the `names` mapping property to be an empty string. Issue #121.
## 0.1.35
* A third optional parameter was added to `SourceNode.fromStringWithSourceMap`
to specify a path that relative sources in the second parameter should be
relative to. Issue #105.
* If no file property is given to a `SourceMapGenerator`, then the resulting
source map will no longer have a `null` file property. The property will
simply not exist. Issue #104.
* Fixed a bug where consecutive newlines were ignored in `SourceNode`s.
Issue #116.
## 0.1.34
* Make `SourceNode` work with windows style ("\r\n") newlines. Issue #103.
* Fix bug involving source contents and the
`SourceMapGenerator.prototype.applySourceMap`. Issue #100.
## 0.1.33
* Fix some edge cases surrounding path joining and URL resolution.
* Add a third parameter for relative path to
`SourceMapGenerator.prototype.applySourceMap`.
* Fix issues with mappings and EOLs.
## 0.1.32
* Fixed a bug where SourceMapConsumer couldn't handle negative relative columns
(issue 92).
* Fixed test runner to actually report number of failed tests as its process
exit code.
* Fixed a typo when reporting bad mappings (issue 87).
## 0.1.31
* Delay parsing the mappings in SourceMapConsumer until queried for a source
location.
* Support Sass source maps (which at the time of writing deviate from the spec
in small ways) in SourceMapConsumer.
## 0.1.30
* Do not join source root with a source, when the source is a data URI.
* Extend the test runner to allow running single specific test files at a time.
* Performance improvements in `SourceNode.prototype.walk` and
`SourceMapConsumer.prototype.eachMapping`.
* Source map browser builds will now work inside Workers.
* Better error messages when attempting to add an invalid mapping to a
`SourceMapGenerator`.
## 0.1.29
* Allow duplicate entries in the `names` and `sources` arrays of source maps
(usually from TypeScript) we are parsing. Fixes github issue 72.
## 0.1.28
* Skip duplicate mappings when creating source maps from SourceNode; github
issue 75.
## 0.1.27
* Don't throw an error when the `file` property is missing in SourceMapConsumer,
we don't use it anyway.
## 0.1.26
* Fix SourceNode.fromStringWithSourceMap for empty maps. Fixes github issue 70.
## 0.1.25
* Make compatible with browserify
## 0.1.24
* Fix issue with absolute paths and `file://` URIs. See
https://bugzilla.mozilla.org/show_bug.cgi?id=885597
## 0.1.23
* Fix issue with absolute paths and sourcesContent, github issue 64.
## 0.1.22
* Ignore duplicate mappings in SourceMapGenerator. Fixes github issue 21.
## 0.1.21
* Fixed handling of sources that start with a slash so that they are relative to
the source root's host.
## 0.1.20
* Fixed github issue #43: absolute URLs aren't joined with the source root
anymore.
## 0.1.19
* Using Travis CI to run tests.
## 0.1.18
* Fixed a bug in the handling of sourceRoot.
## 0.1.17
* Added SourceNode.fromStringWithSourceMap.
## 0.1.16
* Added missing documentation.
* Fixed the generating of empty mappings in SourceNode.
## 0.1.15
* Added SourceMapGenerator.applySourceMap.
## 0.1.14
* The sourceRoot is now handled consistently.
## 0.1.13
* Added SourceMapGenerator.fromSourceMap.
## 0.1.12
* SourceNode now generates empty mappings too.
## 0.1.11
* Added name support to SourceNode.
## 0.1.10
* Added sourcesContent support to the customer and generator.

View File

@@ -0,0 +1,28 @@
Copyright (c) 2009-2011, Mozilla Foundation and contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the names of the Mozilla Foundation nor the names of project
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,729 @@
# Source Map
[![Build Status](https://travis-ci.org/mozilla/source-map.png?branch=master)](https://travis-ci.org/mozilla/source-map)
[![NPM](https://nodei.co/npm/source-map.png?downloads=true&downloadRank=true)](https://www.npmjs.com/package/source-map)
This is a library to generate and consume the source map format
[described here][format].
[format]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
## Use with Node
$ npm install source-map
## Use on the Web
<script src="https://raw.githubusercontent.com/mozilla/source-map/master/dist/source-map.min.js" defer></script>
--------------------------------------------------------------------------------
<!-- `npm run toc` to regenerate the Table of Contents -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
## Table of Contents
- [Examples](#examples)
- [Consuming a source map](#consuming-a-source-map)
- [Generating a source map](#generating-a-source-map)
- [With SourceNode (high level API)](#with-sourcenode-high-level-api)
- [With SourceMapGenerator (low level API)](#with-sourcemapgenerator-low-level-api)
- [API](#api)
- [SourceMapConsumer](#sourcemapconsumer)
- [new SourceMapConsumer(rawSourceMap)](#new-sourcemapconsumerrawsourcemap)
- [SourceMapConsumer.prototype.computeColumnSpans()](#sourcemapconsumerprototypecomputecolumnspans)
- [SourceMapConsumer.prototype.originalPositionFor(generatedPosition)](#sourcemapconsumerprototypeoriginalpositionforgeneratedposition)
- [SourceMapConsumer.prototype.generatedPositionFor(originalPosition)](#sourcemapconsumerprototypegeneratedpositionfororiginalposition)
- [SourceMapConsumer.prototype.allGeneratedPositionsFor(originalPosition)](#sourcemapconsumerprototypeallgeneratedpositionsfororiginalposition)
- [SourceMapConsumer.prototype.hasContentsOfAllSources()](#sourcemapconsumerprototypehascontentsofallsources)
- [SourceMapConsumer.prototype.sourceContentFor(source[, returnNullOnMissing])](#sourcemapconsumerprototypesourcecontentforsource-returnnullonmissing)
- [SourceMapConsumer.prototype.eachMapping(callback, context, order)](#sourcemapconsumerprototypeeachmappingcallback-context-order)
- [SourceMapGenerator](#sourcemapgenerator)
- [new SourceMapGenerator([startOfSourceMap])](#new-sourcemapgeneratorstartofsourcemap)
- [SourceMapGenerator.fromSourceMap(sourceMapConsumer)](#sourcemapgeneratorfromsourcemapsourcemapconsumer)
- [SourceMapGenerator.prototype.addMapping(mapping)](#sourcemapgeneratorprototypeaddmappingmapping)
- [SourceMapGenerator.prototype.setSourceContent(sourceFile, sourceContent)](#sourcemapgeneratorprototypesetsourcecontentsourcefile-sourcecontent)
- [SourceMapGenerator.prototype.applySourceMap(sourceMapConsumer[, sourceFile[, sourceMapPath]])](#sourcemapgeneratorprototypeapplysourcemapsourcemapconsumer-sourcefile-sourcemappath)
- [SourceMapGenerator.prototype.toString()](#sourcemapgeneratorprototypetostring)
- [SourceNode](#sourcenode)
- [new SourceNode([line, column, source[, chunk[, name]]])](#new-sourcenodeline-column-source-chunk-name)
- [SourceNode.fromStringWithSourceMap(code, sourceMapConsumer[, relativePath])](#sourcenodefromstringwithsourcemapcode-sourcemapconsumer-relativepath)
- [SourceNode.prototype.add(chunk)](#sourcenodeprototypeaddchunk)
- [SourceNode.prototype.prepend(chunk)](#sourcenodeprototypeprependchunk)
- [SourceNode.prototype.setSourceContent(sourceFile, sourceContent)](#sourcenodeprototypesetsourcecontentsourcefile-sourcecontent)
- [SourceNode.prototype.walk(fn)](#sourcenodeprototypewalkfn)
- [SourceNode.prototype.walkSourceContents(fn)](#sourcenodeprototypewalksourcecontentsfn)
- [SourceNode.prototype.join(sep)](#sourcenodeprototypejoinsep)
- [SourceNode.prototype.replaceRight(pattern, replacement)](#sourcenodeprototypereplacerightpattern-replacement)
- [SourceNode.prototype.toString()](#sourcenodeprototypetostring)
- [SourceNode.prototype.toStringWithSourceMap([startOfSourceMap])](#sourcenodeprototypetostringwithsourcemapstartofsourcemap)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## Examples
### Consuming a source map
```js
var rawSourceMap = {
version: 3,
file: 'min.js',
names: ['bar', 'baz', 'n'],
sources: ['one.js', 'two.js'],
sourceRoot: 'http://example.com/www/js/',
mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
};
var smc = new SourceMapConsumer(rawSourceMap);
console.log(smc.sources);
// [ 'http://example.com/www/js/one.js',
// 'http://example.com/www/js/two.js' ]
console.log(smc.originalPositionFor({
line: 2,
column: 28
}));
// { source: 'http://example.com/www/js/two.js',
// line: 2,
// column: 10,
// name: 'n' }
console.log(smc.generatedPositionFor({
source: 'http://example.com/www/js/two.js',
line: 2,
column: 10
}));
// { line: 2, column: 28 }
smc.eachMapping(function (m) {
// ...
});
```
### Generating a source map
In depth guide:
[**Compiling to JavaScript, and Debugging with Source Maps**](https://hacks.mozilla.org/2013/05/compiling-to-javascript-and-debugging-with-source-maps/)
#### With SourceNode (high level API)
```js
function compile(ast) {
switch (ast.type) {
case 'BinaryExpression':
return new SourceNode(
ast.location.line,
ast.location.column,
ast.location.source,
[compile(ast.left), " + ", compile(ast.right)]
);
case 'Literal':
return new SourceNode(
ast.location.line,
ast.location.column,
ast.location.source,
String(ast.value)
);
// ...
default:
throw new Error("Bad AST");
}
}
var ast = parse("40 + 2", "add.js");
console.log(compile(ast).toStringWithSourceMap({
file: 'add.js'
}));
// { code: '40 + 2',
// map: [object SourceMapGenerator] }
```
#### With SourceMapGenerator (low level API)
```js
var map = new SourceMapGenerator({
file: "source-mapped.js"
});
map.addMapping({
generated: {
line: 10,
column: 35
},
source: "foo.js",
original: {
line: 33,
column: 2
},
name: "christopher"
});
console.log(map.toString());
// '{"version":3,"file":"source-mapped.js","sources":["foo.js"],"names":["christopher"],"mappings":";;;;;;;;;mCAgCEA"}'
```
## API
Get a reference to the module:
```js
// Node.js
var sourceMap = require('source-map');
// Browser builds
var sourceMap = window.sourceMap;
// Inside Firefox
const sourceMap = require("devtools/toolkit/sourcemap/source-map.js");
```
### SourceMapConsumer
A SourceMapConsumer instance represents a parsed source map which we can query
for information about the original file positions by giving it a file position
in the generated source.
#### new SourceMapConsumer(rawSourceMap)
The only parameter is the raw source map (either as a string which can be
`JSON.parse`'d, or an object). According to the spec, source maps have the
following attributes:
* `version`: Which version of the source map spec this map is following.
* `sources`: An array of URLs to the original source files.
* `names`: An array of identifiers which can be referenced by individual
mappings.
* `sourceRoot`: Optional. The URL root from which all sources are relative.
* `sourcesContent`: Optional. An array of contents of the original source files.
* `mappings`: A string of base64 VLQs which contain the actual mappings.
* `file`: Optional. The generated filename this source map is associated with.
```js
var consumer = new sourceMap.SourceMapConsumer(rawSourceMapJsonData);
```
#### SourceMapConsumer.prototype.computeColumnSpans()
Compute the last column for each generated mapping. The last column is
inclusive.
```js
// Before:
consumer.allGeneratedPositionsFor({ line: 2, source: "foo.coffee" })
// [ { line: 2,
// column: 1 },
// { line: 2,
// column: 10 },
// { line: 2,
// column: 20 } ]
consumer.computeColumnSpans();
// After:
consumer.allGeneratedPositionsFor({ line: 2, source: "foo.coffee" })
// [ { line: 2,
// column: 1,
// lastColumn: 9 },
// { line: 2,
// column: 10,
// lastColumn: 19 },
// { line: 2,
// column: 20,
// lastColumn: Infinity } ]
```
#### SourceMapConsumer.prototype.originalPositionFor(generatedPosition)
Returns the original source, line, and column information for the generated
source's line and column positions provided. The only argument is an object with
the following properties:
* `line`: The line number in the generated source.
* `column`: The column number in the generated source.
* `bias`: Either `SourceMapConsumer.GREATEST_LOWER_BOUND` or
`SourceMapConsumer.LEAST_UPPER_BOUND`. Specifies whether to return the closest
element that is smaller than or greater than the one we are searching for,
respectively, if the exact element cannot be found. Defaults to
`SourceMapConsumer.GREATEST_LOWER_BOUND`.
and an object is returned with the following properties:
* `source`: The original source file, or null if this information is not
available.
* `line`: The line number in the original source, or null if this information is
not available.
* `column`: The column number in the original source, or null if this
information is not available.
* `name`: The original identifier, or null if this information is not available.
```js
consumer.originalPositionFor({ line: 2, column: 10 })
// { source: 'foo.coffee',
// line: 2,
// column: 2,
// name: null }
consumer.originalPositionFor({ line: 99999999999999999, column: 999999999999999 })
// { source: null,
// line: null,
// column: null,
// name: null }
```
#### SourceMapConsumer.prototype.generatedPositionFor(originalPosition)
Returns the generated line and column information for the original source,
line, and column positions provided. The only argument is an object with
the following properties:
* `source`: The filename of the original source.
* `line`: The line number in the original source.
* `column`: The column number in the original source.
and an object is returned with the following properties:
* `line`: The line number in the generated source, or null.
* `column`: The column number in the generated source, or null.
```js
consumer.generatedPositionFor({ source: "example.js", line: 2, column: 10 })
// { line: 1,
// column: 56 }
```
#### SourceMapConsumer.prototype.allGeneratedPositionsFor(originalPosition)
Returns all generated line and column information for the original source, line,
and column provided. If no column is provided, returns all mappings
corresponding to a either the line we are searching for or the next closest line
that has any mappings. Otherwise, returns all mappings corresponding to the
given line and either the column we are searching for or the next closest column
that has any offsets.
The only argument is an object with the following properties:
* `source`: The filename of the original source.
* `line`: The line number in the original source.
* `column`: Optional. The column number in the original source.
and an array of objects is returned, each with the following properties:
* `line`: The line number in the generated source, or null.
* `column`: The column number in the generated source, or null.
```js
consumer.allGeneratedpositionsfor({ line: 2, source: "foo.coffee" })
// [ { line: 2,
// column: 1 },
// { line: 2,
// column: 10 },
// { line: 2,
// column: 20 } ]
```
#### SourceMapConsumer.prototype.hasContentsOfAllSources()
Return true if we have the embedded source content for every source listed in
the source map, false otherwise.
In other words, if this method returns `true`, then
`consumer.sourceContentFor(s)` will succeed for every source `s` in
`consumer.sources`.
```js
// ...
if (consumer.hasContentsOfAllSources()) {
consumerReadyCallback(consumer);
} else {
fetchSources(consumer, consumerReadyCallback);
}
// ...
```
#### SourceMapConsumer.prototype.sourceContentFor(source[, returnNullOnMissing])
Returns the original source content for the source provided. The only
argument is the URL of the original source file.
If the source content for the given source is not found, then an error is
thrown. Optionally, pass `true` as the second param to have `null` returned
instead.
```js
consumer.sources
// [ "my-cool-lib.clj" ]
consumer.sourceContentFor("my-cool-lib.clj")
// "..."
consumer.sourceContentFor("this is not in the source map");
// Error: "this is not in the source map" is not in the source map
consumer.sourceContentFor("this is not in the source map", true);
// null
```
#### SourceMapConsumer.prototype.eachMapping(callback, context, order)
Iterate over each mapping between an original source/line/column and a
generated line/column in this source map.
* `callback`: The function that is called with each mapping. Mappings have the
form `{ source, generatedLine, generatedColumn, originalLine, originalColumn,
name }`
* `context`: Optional. If specified, this object will be the value of `this`
every time that `callback` is called.
* `order`: Either `SourceMapConsumer.GENERATED_ORDER` or
`SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to iterate over
the mappings sorted by the generated file's line/column order or the
original's source/line/column order, respectively. Defaults to
`SourceMapConsumer.GENERATED_ORDER`.
```js
consumer.eachMapping(function (m) { console.log(m); })
// ...
// { source: 'illmatic.js',
// generatedLine: 1,
// generatedColumn: 0,
// originalLine: 1,
// originalColumn: 0,
// name: null }
// { source: 'illmatic.js',
// generatedLine: 2,
// generatedColumn: 0,
// originalLine: 2,
// originalColumn: 0,
// name: null }
// ...
```
### SourceMapGenerator
An instance of the SourceMapGenerator represents a source map which is being
built incrementally.
#### new SourceMapGenerator([startOfSourceMap])
You may pass an object with the following properties:
* `file`: The filename of the generated source that this source map is
associated with.
* `sourceRoot`: A root for all relative URLs in this source map.
* `skipValidation`: Optional. When `true`, disables validation of mappings as
they are added. This can improve performance but should be used with
discretion, as a last resort. Even then, one should avoid using this flag when
running tests, if possible.
```js
var generator = new sourceMap.SourceMapGenerator({
file: "my-generated-javascript-file.js",
sourceRoot: "http://example.com/app/js/"
});
```
#### SourceMapGenerator.fromSourceMap(sourceMapConsumer)
Creates a new `SourceMapGenerator` from an existing `SourceMapConsumer` instance.
* `sourceMapConsumer` The SourceMap.
```js
var generator = sourceMap.SourceMapGenerator.fromSourceMap(consumer);
```
#### SourceMapGenerator.prototype.addMapping(mapping)
Add a single mapping from original source line and column to the generated
source's line and column for this source map being created. The mapping object
should have the following properties:
* `generated`: An object with the generated line and column positions.
* `original`: An object with the original line and column positions.
* `source`: The original source file (relative to the sourceRoot).
* `name`: An optional original token name for this mapping.
```js
generator.addMapping({
source: "module-one.scm",
original: { line: 128, column: 0 },
generated: { line: 3, column: 456 }
})
```
#### SourceMapGenerator.prototype.setSourceContent(sourceFile, sourceContent)
Set the source content for an original source file.
* `sourceFile` the URL of the original source file.
* `sourceContent` the content of the source file.
```js
generator.setSourceContent("module-one.scm",
fs.readFileSync("path/to/module-one.scm"))
```
#### SourceMapGenerator.prototype.applySourceMap(sourceMapConsumer[, sourceFile[, sourceMapPath]])
Applies a SourceMap for a source file to the SourceMap.
Each mapping to the supplied source file is rewritten using the
supplied SourceMap. Note: The resolution for the resulting mappings
is the minimum of this map and the supplied map.
* `sourceMapConsumer`: The SourceMap to be applied.
* `sourceFile`: Optional. The filename of the source file.
If omitted, sourceMapConsumer.file will be used, if it exists.
Otherwise an error will be thrown.
* `sourceMapPath`: Optional. The dirname of the path to the SourceMap
to be applied. If relative, it is relative to the SourceMap.
This parameter is needed when the two SourceMaps aren't in the same
directory, and the SourceMap to be applied contains relative source
paths. If so, those relative source paths need to be rewritten
relative to the SourceMap.
If omitted, it is assumed that both SourceMaps are in the same directory,
thus not needing any rewriting. (Supplying `'.'` has the same effect.)
#### SourceMapGenerator.prototype.toString()
Renders the source map being generated to a string.
```js
generator.toString()
// '{"version":3,"sources":["module-one.scm"],"names":[],"mappings":"...snip...","file":"my-generated-javascript-file.js","sourceRoot":"http://example.com/app/js/"}'
```
### SourceNode
SourceNodes provide a way to abstract over interpolating and/or concatenating
snippets of generated JavaScript source code, while maintaining the line and
column information associated between those snippets and the original source
code. This is useful as the final intermediate representation a compiler might
use before outputting the generated JS and source map.
#### new SourceNode([line, column, source[, chunk[, name]]])
* `line`: The original line number associated with this source node, or null if
it isn't associated with an original line.
* `column`: The original column number associated with this source node, or null
if it isn't associated with an original column.
* `source`: The original source's filename; null if no filename is provided.
* `chunk`: Optional. Is immediately passed to `SourceNode.prototype.add`, see
below.
* `name`: Optional. The original identifier.
```js
var node = new SourceNode(1, 2, "a.cpp", [
new SourceNode(3, 4, "b.cpp", "extern int status;\n"),
new SourceNode(5, 6, "c.cpp", "std::string* make_string(size_t n);\n"),
new SourceNode(7, 8, "d.cpp", "int main(int argc, char** argv) {}\n"),
]);
```
#### SourceNode.fromStringWithSourceMap(code, sourceMapConsumer[, relativePath])
Creates a SourceNode from generated code and a SourceMapConsumer.
* `code`: The generated code
* `sourceMapConsumer` The SourceMap for the generated code
* `relativePath` The optional path that relative sources in `sourceMapConsumer`
should be relative to.
```js
var consumer = new SourceMapConsumer(fs.readFileSync("path/to/my-file.js.map", "utf8"));
var node = SourceNode.fromStringWithSourceMap(fs.readFileSync("path/to/my-file.js"),
consumer);
```
#### SourceNode.prototype.add(chunk)
Add a chunk of generated JS to this source node.
* `chunk`: A string snippet of generated JS code, another instance of
`SourceNode`, or an array where each member is one of those things.
```js
node.add(" + ");
node.add(otherNode);
node.add([leftHandOperandNode, " + ", rightHandOperandNode]);
```
#### SourceNode.prototype.prepend(chunk)
Prepend a chunk of generated JS to this source node.
* `chunk`: A string snippet of generated JS code, another instance of
`SourceNode`, or an array where each member is one of those things.
```js
node.prepend("/** Build Id: f783haef86324gf **/\n\n");
```
#### SourceNode.prototype.setSourceContent(sourceFile, sourceContent)
Set the source content for a source file. This will be added to the
`SourceMap` in the `sourcesContent` field.
* `sourceFile`: The filename of the source file
* `sourceContent`: The content of the source file
```js
node.setSourceContent("module-one.scm",
fs.readFileSync("path/to/module-one.scm"))
```
#### SourceNode.prototype.walk(fn)
Walk over the tree of JS snippets in this node and its children. The walking
function is called once for each snippet of JS and is passed that snippet and
the its original associated source's line/column location.
* `fn`: The traversal function.
```js
var node = new SourceNode(1, 2, "a.js", [
new SourceNode(3, 4, "b.js", "uno"),
"dos",
[
"tres",
new SourceNode(5, 6, "c.js", "quatro")
]
]);
node.walk(function (code, loc) { console.log("WALK:", code, loc); })
// WALK: uno { source: 'b.js', line: 3, column: 4, name: null }
// WALK: dos { source: 'a.js', line: 1, column: 2, name: null }
// WALK: tres { source: 'a.js', line: 1, column: 2, name: null }
// WALK: quatro { source: 'c.js', line: 5, column: 6, name: null }
```
#### SourceNode.prototype.walkSourceContents(fn)
Walk over the tree of SourceNodes. The walking function is called for each
source file content and is passed the filename and source content.
* `fn`: The traversal function.
```js
var a = new SourceNode(1, 2, "a.js", "generated from a");
a.setSourceContent("a.js", "original a");
var b = new SourceNode(1, 2, "b.js", "generated from b");
b.setSourceContent("b.js", "original b");
var c = new SourceNode(1, 2, "c.js", "generated from c");
c.setSourceContent("c.js", "original c");
var node = new SourceNode(null, null, null, [a, b, c]);
node.walkSourceContents(function (source, contents) { console.log("WALK:", source, ":", contents); })
// WALK: a.js : original a
// WALK: b.js : original b
// WALK: c.js : original c
```
#### SourceNode.prototype.join(sep)
Like `Array.prototype.join` except for SourceNodes. Inserts the separator
between each of this source node's children.
* `sep`: The separator.
```js
var lhs = new SourceNode(1, 2, "a.rs", "my_copy");
var operand = new SourceNode(3, 4, "a.rs", "=");
var rhs = new SourceNode(5, 6, "a.rs", "orig.clone()");
var node = new SourceNode(null, null, null, [ lhs, operand, rhs ]);
var joinedNode = node.join(" ");
```
#### SourceNode.prototype.replaceRight(pattern, replacement)
Call `String.prototype.replace` on the very right-most source snippet. Useful
for trimming white space from the end of a source node, etc.
* `pattern`: The pattern to replace.
* `replacement`: The thing to replace the pattern with.
```js
// Trim trailing white space.
node.replaceRight(/\s*$/, "");
```
#### SourceNode.prototype.toString()
Return the string representation of this source node. Walks over the tree and
concatenates all the various snippets together to one string.
```js
var node = new SourceNode(1, 2, "a.js", [
new SourceNode(3, 4, "b.js", "uno"),
"dos",
[
"tres",
new SourceNode(5, 6, "c.js", "quatro")
]
]);
node.toString()
// 'unodostresquatro'
```
#### SourceNode.prototype.toStringWithSourceMap([startOfSourceMap])
Returns the string representation of this tree of source nodes, plus a
SourceMapGenerator which contains all the mappings between the generated and
original sources.
The arguments are the same as those to `new SourceMapGenerator`.
```js
var node = new SourceNode(1, 2, "a.js", [
new SourceNode(3, 4, "b.js", "uno"),
"dos",
[
"tres",
new SourceNode(5, 6, "c.js", "quatro")
]
]);
node.toStringWithSourceMap({ file: "my-output-file.js" })
// { code: 'unodostresquatro',
// map: [object SourceMapGenerator] }
```

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,121 @@
/* -*- Mode: js; js-indent-level: 2; -*- */
/*
* Copyright 2011 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE or:
* http://opensource.org/licenses/BSD-3-Clause
*/
var util = require('./util');
var has = Object.prototype.hasOwnProperty;
var hasNativeMap = typeof Map !== "undefined";
/**
* A data structure which is a combination of an array and a set. Adding a new
* member is O(1), testing for membership is O(1), and finding the index of an
* element is O(1). Removing elements from the set is not supported. Only
* strings are supported for membership.
*/
function ArraySet() {
this._array = [];
this._set = hasNativeMap ? new Map() : Object.create(null);
}
/**
* Static method for creating ArraySet instances from an existing array.
*/
ArraySet.fromArray = function ArraySet_fromArray(aArray, aAllowDuplicates) {
var set = new ArraySet();
for (var i = 0, len = aArray.length; i < len; i++) {
set.add(aArray[i], aAllowDuplicates);
}
return set;
};
/**
* Return how many unique items are in this ArraySet. If duplicates have been
* added, than those do not count towards the size.
*
* @returns Number
*/
ArraySet.prototype.size = function ArraySet_size() {
return hasNativeMap ? this._set.size : Object.getOwnPropertyNames(this._set).length;
};
/**
* Add the given string to this set.
*
* @param String aStr
*/
ArraySet.prototype.add = function ArraySet_add(aStr, aAllowDuplicates) {
var sStr = hasNativeMap ? aStr : util.toSetString(aStr);
var isDuplicate = hasNativeMap ? this.has(aStr) : has.call(this._set, sStr);
var idx = this._array.length;
if (!isDuplicate || aAllowDuplicates) {
this._array.push(aStr);
}
if (!isDuplicate) {
if (hasNativeMap) {
this._set.set(aStr, idx);
} else {
this._set[sStr] = idx;
}
}
};
/**
* Is the given string a member of this set?
*
* @param String aStr
*/
ArraySet.prototype.has = function ArraySet_has(aStr) {
if (hasNativeMap) {
return this._set.has(aStr);
} else {
var sStr = util.toSetString(aStr);
return has.call(this._set, sStr);
}
};
/**
* What is the index of the given string in the array?
*
* @param String aStr
*/
ArraySet.prototype.indexOf = function ArraySet_indexOf(aStr) {
if (hasNativeMap) {
var idx = this._set.get(aStr);
if (idx >= 0) {
return idx;
}
} else {
var sStr = util.toSetString(aStr);
if (has.call(this._set, sStr)) {
return this._set[sStr];
}
}
throw new Error('"' + aStr + '" is not in the set.');
};
/**
* What is the element at the given index?
*
* @param Number aIdx
*/
ArraySet.prototype.at = function ArraySet_at(aIdx) {
if (aIdx >= 0 && aIdx < this._array.length) {
return this._array[aIdx];
}
throw new Error('No element indexed by ' + aIdx);
};
/**
* Returns the array representation of this set (which has the proper indices
* indicated by indexOf). Note that this is a copy of the internal array used
* for storing the members so that no one can mess with internal state.
*/
ArraySet.prototype.toArray = function ArraySet_toArray() {
return this._array.slice();
};
exports.ArraySet = ArraySet;

Some files were not shown because too many files have changed in this diff Show More