Import Upstream version 27.5.1~ds+~cs69.51.22

This commit is contained in:
Lu zhiping 2022-07-16 13:02:45 +08:00
commit c0606860b8
2599 changed files with 332364 additions and 0 deletions

19
.editorconfig Normal file
View File

@ -0,0 +1,19 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
#
# Some of these options are also respected by Prettier
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.{md,snap}]
trim_trailing_whitespace = false

14
.eslintignore Normal file
View File

@ -0,0 +1,14 @@
**/coverage/**
**/node_modules/**
bin/
flow-typed/**
packages/*/build/**
packages/*/dist/**
packages/jest-diff/src/cleanupSemantic.ts
website/.docusaurus
website/blog
website/build
website/node_modules
website/i18n/*.js
website/static
!.eslintrc.js

14
.eslintplugin/index.js Normal file
View File

@ -0,0 +1,14 @@
/**
* Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
exports.rules = {
'ban-types-eventually': require('@typescript-eslint/eslint-plugin').rules[
'ban-types'
],
'prefer-rest-params-eventually': require('eslint/lib/rules/prefer-rest-params'),
'prefer-spread-eventually': require('eslint/lib/rules/prefer-spread'),
};

511
.eslintrc.js Normal file
View File

@ -0,0 +1,511 @@
/**
* Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const {getPackages} = require('./scripts/buildUtils');
const internalPackages = getPackages()
.map(({pkg}) => pkg.name)
.sort();
module.exports = {
env: {
'jest/globals': true,
node: true,
},
extends: [
'plugin:import/errors',
'plugin:eslint-comments/recommended',
'plugin:prettier/recommended',
],
globals: {
BigInt: 'readonly',
},
overrides: [
{
extends: [
'plugin:@typescript-eslint/eslint-recommended',
'plugin:import/typescript',
],
files: ['*.ts', '*.tsx'],
plugins: ['@typescript-eslint/eslint-plugin', 'local'],
rules: {
'@typescript-eslint/array-type': ['error', {default: 'generic'}],
'@typescript-eslint/ban-types': 'error',
'@typescript-eslint/no-implicit-any-catch': [
'error',
{allowExplicitAny: true},
],
'@typescript-eslint/no-unused-vars': [
'error',
{argsIgnorePattern: '^_'},
],
'@typescript-eslint/prefer-ts-expect-error': 'error',
// TS verifies this
'consistent-return': 'off',
// Since we do `export =`. Remove for Jest 27
'import/default': 'off',
'no-dupe-class-members': 'off',
'no-unused-vars': 'off',
},
},
{
files: [
'packages/jest-jasmine2/src/jasmine/Env.ts',
'packages/jest-jasmine2/src/jasmine/ReportDispatcher.ts',
'packages/jest-jasmine2/src/jasmine/Spec.ts',
'packages/jest-jasmine2/src/jasmine/SpyStrategy.ts',
'packages/jest-jasmine2/src/jasmine/Suite.ts',
'packages/jest-jasmine2/src/jasmine/createSpy.ts',
'packages/jest-jasmine2/src/jasmine/jasmineLight.ts',
'packages/jest-mock/src/__tests__/index.test.ts',
'packages/jest-mock/src/index.ts',
'packages/pretty-format/src/__tests__/Immutable.test.ts',
'packages/pretty-format/src/__tests__/prettyFormat.test.ts',
],
rules: {
'local/prefer-rest-params-eventually': 'warn',
'prefer-rest-params': 'off',
},
},
{
files: [
'packages/expect/src/index.ts',
'packages/jest-fake-timers/src/legacyFakeTimers.ts',
'packages/jest-jasmine2/src/jasmine/Env.ts',
'packages/jest-jasmine2/src/jasmine/ReportDispatcher.ts',
'packages/jest-jasmine2/src/jasmine/Spec.ts',
'packages/jest-jasmine2/src/jasmine/Suite.ts',
'packages/jest-jasmine2/src/jasmine/jasmineLight.ts',
'packages/jest-jasmine2/src/jestExpect.ts',
'packages/jest-resolve/src/resolver.ts',
],
rules: {
'local/prefer-spread-eventually': 'warn',
'prefer-spread': 'off',
},
},
{
files: [
'e2e/babel-plugin-jest-hoist/__tests__/typescript.test.ts',
'e2e/coverage-remapping/covered.ts',
'packages/expect/src/matchers.ts',
'packages/expect/src/print.ts',
'packages/expect/src/toThrowMatchers.ts',
'packages/expect/src/utils.ts',
'packages/jest-core/src/ReporterDispatcher.ts',
'packages/jest-core/src/TestScheduler.ts',
'packages/jest-core/src/collectHandles.ts',
'packages/jest-core/src/plugins/UpdateSnapshotsInteractive.ts',
'packages/jest-fake-timers/src/legacyFakeTimers.ts',
'packages/jest-haste-map/src/index.ts',
'packages/jest-haste-map/src/watchers/FSEventsWatcher.ts',
'packages/jest-jasmine2/src/jasmine/SpyStrategy.ts',
'packages/jest-jasmine2/src/jasmine/Suite.ts',
'packages/jest-leak-detector/src/index.ts',
'packages/jest-matcher-utils/src/index.ts',
'packages/jest-mock/src/__tests__/index.test.ts',
'packages/jest-mock/src/index.ts',
'packages/jest-snapshot/src/index.ts',
'packages/jest-snapshot/src/printSnapshot.ts',
'packages/jest-snapshot/src/types.ts',
'packages/jest-util/src/convertDescriptorToString.ts',
'packages/jest-worker/src/Farm.ts',
'packages/jest-worker/src/index.ts',
'packages/pretty-format/src/index.ts',
'packages/pretty-format/src/plugins/DOMCollection.ts',
],
rules: {
'@typescript-eslint/ban-types': [
'error',
// TODO: remove these overrides: https://github.com/facebook/jest/issues/10177
{types: {Function: false, object: false, '{}': false}},
],
'local/ban-types-eventually': [
'warn',
{
types: {
// none of these types are in use, so can be errored on
Boolean: false,
Number: false,
Object: false,
String: false,
Symbol: false,
},
},
],
},
},
// to make it more suitable for running on code examples in docs/ folder
{
files: ['*.md'],
rules: {
'arrow-body-style': 'off',
'consistent-return': 'off',
'import/no-extraneous-dependencies': 'off',
'import/no-unresolved': 'off',
'no-console': 'off',
'no-undef': 'off',
'no-unused-vars': 'off',
'prettier/prettier': 'off',
'sort-keys': 'off',
},
},
// snapshots in examples plus inline snapshots need to keep backtick
{
files: ['*.md', 'e2e/custom-inline-snapshot-matchers/__tests__/*'],
rules: {
quotes: [
'error',
'single',
{allowTemplateLiterals: true, avoidEscape: true},
],
},
},
{
files: ['website/**/*'],
rules: {
'import/order': 'off',
'import/sort-keys': 'off',
'sort-keys': 'off',
},
},
{
files: ['examples/**/*'],
rules: {
'import/no-unresolved': ['error', {ignore: ['^react-native$']}],
'import/order': 'off',
},
},
{
files: 'packages/jest-types/**/*',
rules: {
'import/no-extraneous-dependencies': 'off',
},
},
{
files: 'packages/**/*.ts',
rules: {
'@typescript-eslint/explicit-module-boundary-types': 'error',
'import/no-anonymous-default-export': [
'error',
{
allowAnonymousClass: false,
allowAnonymousFunction: false,
allowArray: false,
allowArrowFunction: false,
allowCallExpression: false,
allowLiteral: false,
allowObject: true,
},
],
},
},
{
files: [
'**/__tests__/**',
'**/__mocks__/**',
'packages/jest-jasmine2/src/jasmine/**/*',
'packages/expect/src/jasmineUtils.ts',
'**/vendor/**/*',
],
rules: {
'@typescript-eslint/explicit-module-boundary-types': 'off',
},
},
{
files: [
'packages/jest-jasmine2/src/jasmine/**/*',
'packages/expect/src/jasmineUtils.ts',
'**/vendor/**/*',
],
rules: {
'eslint-comments/disable-enable-pair': 'off',
'eslint-comments/no-unlimited-disable': 'off',
},
},
{
files: [
'e2e/error-on-deprecated/__tests__/*',
'e2e/jasmine-async/__tests__/*',
],
globals: {
fail: true,
jasmine: true,
pending: true,
},
},
{
files: [
'website/**',
'**/__tests__/**',
'e2e/**',
'**/pretty-format/perf/**',
],
rules: {
'import/no-extraneous-dependencies': 'off',
},
},
{
files: ['**/__typetests__/**', '*.md'],
rules: {
'jest/no-focused-tests': 'off',
'jest/no-identical-title': 'off',
'jest/valid-expect': 'off',
},
},
{
files: [
'scripts/*',
'packages/jest-cli/src/init/index.ts',
'packages/jest-repl/src/cli/runtime-cli.ts',
],
rules: {
'no-console': 'off',
},
},
{
files: [
'e2e/**',
'examples/**',
'scripts/*',
'website/**',
'**/__mocks__/**',
'**/__tests__/**',
'**/__typetests__/**',
'**/__performance_tests__/**',
'packages/diff-sequences/perf/index.js',
'packages/pretty-format/perf/test.js',
],
rules: {
'@typescript-eslint/no-unused-vars': 'off',
'import/no-unresolved': 'off',
'no-console': 'off',
'no-unused-vars': 'off',
},
},
],
parser: '@typescript-eslint/parser',
parserOptions: {
sourceType: 'module',
},
plugins: ['markdown', 'import', 'jest'],
rules: {
'accessor-pairs': ['warn', {setWithoutGet: true}],
'block-scoped-var': 'off',
'callback-return': 'off',
camelcase: ['off', {properties: 'always'}],
complexity: 'off',
'consistent-return': 'warn',
'consistent-this': ['off', 'self'],
'constructor-super': 'error',
'default-case': 'off',
'dot-notation': 'off',
eqeqeq: ['off', 'allow-null'],
'eslint-comments/disable-enable-pair': ['error', {allowWholeFile: true}],
'eslint-comments/no-unused-disable': 'error',
'func-names': 'off',
'func-style': ['off', 'declaration'],
'global-require': 'off',
'guard-for-in': 'off',
'handle-callback-err': 'off',
'id-length': 'off',
'id-match': 'off',
'import/no-duplicates': 'error',
'import/no-extraneous-dependencies': [
'error',
{
devDependencies: [
'**/__mocks__/**',
'**/__tests__/**',
'**/__typetests__/**',
'**/?(*.)(spec|test).js?(x)',
'scripts/**',
'babel.config.js',
'testSetupFile.js',
'.eslintrc.js',
],
},
],
'import/no-unresolved': ['error', {ignore: ['fsevents']}],
'import/order': [
'error',
{
alphabetize: {
order: 'asc',
},
// this is the default order except for added `internal` in the middle
groups: [
'builtin',
'external',
'internal',
'parent',
'sibling',
'index',
],
'newlines-between': 'never',
},
],
'init-declarations': 'off',
'jest/no-focused-tests': 'error',
'jest/no-identical-title': 'error',
'jest/valid-expect': 'error',
'lines-around-comment': 'off',
'max-depth': 'off',
'max-nested-callbacks': 'off',
'max-params': 'off',
'max-statements': 'off',
'new-cap': 'off',
'new-parens': 'error',
'newline-after-var': 'off',
'no-alert': 'off',
'no-array-constructor': 'error',
'no-bitwise': 'warn',
'no-caller': 'error',
'no-case-declarations': 'off',
'no-catch-shadow': 'error',
'no-class-assign': 'warn',
'no-cond-assign': 'off',
'no-confusing-arrow': 'off',
'no-console': [
'warn',
{allow: ['warn', 'error', 'time', 'timeEnd', 'timeStamp']},
],
'no-const-assign': 'error',
'no-constant-condition': 'off',
'no-continue': 'off',
'no-control-regex': 'off',
'no-debugger': 'error',
'no-delete-var': 'error',
'no-div-regex': 'off',
'no-dupe-args': 'error',
'no-dupe-class-members': 'error',
'no-dupe-keys': 'error',
'no-duplicate-case': 'warn',
'no-else-return': 'off',
'no-empty': 'off',
'no-empty-character-class': 'warn',
'no-empty-pattern': 'warn',
'no-eq-null': 'off',
'no-eval': 'error',
'no-ex-assign': 'warn',
'no-extend-native': 'warn',
'no-extra-bind': 'warn',
'no-extra-boolean-cast': 'warn',
'no-fallthrough': 'warn',
'no-floating-decimal': 'error',
'no-func-assign': 'error',
'no-implicit-coercion': 'off',
'no-implied-eval': 'error',
'no-inline-comments': 'off',
'no-inner-declarations': 'off',
'no-invalid-regexp': 'warn',
'no-invalid-this': 'off',
'no-irregular-whitespace': 'error',
'no-iterator': 'off',
'no-label-var': 'warn',
'no-labels': ['error', {allowLoop: true, allowSwitch: true}],
'no-lonely-if': 'off',
'no-loop-func': 'off',
'no-magic-numbers': 'off',
'no-mixed-requires': 'off',
'no-mixed-spaces-and-tabs': 'error',
'no-multi-str': 'error',
'no-multiple-empty-lines': 'off',
'no-native-reassign': ['error', {exceptions: ['Map', 'Set']}],
'no-negated-condition': 'off',
'no-negated-in-lhs': 'error',
'no-nested-ternary': 'off',
'no-new': 'warn',
'no-new-func': 'error',
'no-new-object': 'warn',
'no-new-require': 'off',
'no-new-wrappers': 'warn',
'no-obj-calls': 'error',
'no-octal': 'warn',
'no-octal-escape': 'warn',
'no-param-reassign': 'off',
'no-path-concat': 'off',
'no-plusplus': 'off',
'no-process-env': 'off',
'no-process-exit': 'off',
'no-proto': 'error',
'no-redeclare': 'warn',
'no-regex-spaces': 'warn',
'no-restricted-imports': [
'error',
{message: 'Please use graceful-fs instead.', name: 'fs'},
],
'no-restricted-modules': 'off',
'no-restricted-syntax': 'off',
'no-return-assign': 'off',
'no-script-url': 'error',
'no-self-compare': 'warn',
'no-sequences': 'warn',
'no-shadow': 'off',
'no-shadow-restricted-names': 'warn',
'no-sparse-arrays': 'error',
'no-sync': 'off',
'no-ternary': 'off',
'no-this-before-super': 'error',
'no-throw-literal': 'error',
'no-undef': 'error',
'no-undef-init': 'off',
'no-undefined': 'off',
'no-underscore-dangle': 'off',
'no-unneeded-ternary': 'warn',
'no-unreachable': 'error',
'no-unused-expressions': 'off',
'no-unused-vars': ['error', {argsIgnorePattern: '^_'}],
'no-use-before-define': 'off',
'no-useless-call': 'warn',
'no-useless-computed-key': 'error',
'no-useless-concat': 'warn',
'no-var': 'error',
'no-void': 'off',
'no-warn-comments': 'off',
'no-with': 'off',
'object-shorthand': 'error',
'one-var': ['warn', {initialized: 'never'}],
'operator-assignment': ['warn', 'always'],
'operator-linebreak': 'off',
'padded-blocks': 'off',
'prefer-arrow-callback': ['error', {allowNamedFunctions: true}],
'prefer-const': 'error',
'prefer-template': 'off',
quotes: [
'error',
'single',
{allowTemplateLiterals: false, avoidEscape: true},
],
radix: 'warn',
'require-jsdoc': 'off',
'require-yield': 'off',
'sort-imports': ['error', {ignoreDeclarationSort: true}],
'sort-keys': 'error',
'sort-vars': 'off',
'spaced-comment': ['off', 'always', {exceptions: ['eslint', 'global']}],
strict: 'off',
'use-isnan': 'error',
'valid-jsdoc': 'off',
'valid-typeof': 'error',
'vars-on-top': 'off',
'wrap-iife': 'off',
'wrap-regex': 'off',
yoda: 'off',
},
settings: {
'import/ignore': ['react-native'],
// using `new RegExp` makes sure to escape `/`
'import/internal-regex': new RegExp(
internalPackages.map(pkg => `^${pkg}$`).join('|'),
).source,
'import/resolver': {
typescript: {},
},
},
};

3
.github/CODEOWNERS vendored Normal file
View File

@ -0,0 +1,3 @@
# Pings orta when PRs are to this module
packages/jest-editor-support/src/* @orta

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
open_collective: jest

5
.github/ISSUE_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,5 @@
## 👉 [Please follow one of these issue templates](https://github.com/facebook/jest/issues/new/choose) 👈
<!-- Love Jest? Please consider supporting our collective: 👉 https://opencollective.com/jest/donate -->
Note: to keep the backlog clean and actionable, issues may be immediately closed if they do not follow one of the above issue templates.

103
.github/ISSUE_TEMPLATE/bug.yml vendored Normal file
View File

@ -0,0 +1,103 @@
name: Bug Report 🪲
description: Create a bug report to help us improve
title: '[Bug]: '
labels: ['Bug Report', 'Needs Triage']
body:
- type: markdown
attributes:
value: |
# Please follow these steps first:
- type: markdown
attributes:
value: |
## Troubleshoot
If Jest is not behaving the way you expect, we'd ask you to look at the [documentation](https://jestjs.io/docs/getting-started) and search the issue tracker for evidence supporting your expectation. Please make reasonable efforts to troubleshoot and rule out issues with your code, the configuration, or any 3rd party libraries you might be using.
- type: markdown
attributes:
value: |
## Ask for help through appropriate channels
If you feel unsure about the cause of the problem, consider asking for help on for example [StackOverflow](https://stackoverflow.com/questions/ask) or our [Discord channel](https://discord.gg/j6FKKQQrW9) before posting a bug report. The issue tracker is not a help forum.
- type: markdown
attributes:
value: |
## Verify transformations
It's important to understand that Jest runs the code in your project as JavaScript. If you use syntax not supported by Node.js out of the box - such as React JSX, types from TypeScript, or components from Angular or Vue - that code needs to be [transformed](https://jestjs.io/docs/code-transformation) into plain JavaScript (similar to what you would do when building for browsers). Jest supports this via the [`transform` configuration option](https://jestjs.io/docs/configuration#transform-objectstring-pathtotransformer--pathtotransformer-object).
- type: markdown
attributes:
value: |
## Make a minimal reproduction
To file the report, you will need a GitHub repository with a minimal (but complete) example and simple/clear steps on how to reproduce the bug. If all your tests fail with a strange error, can you find a way to show us with just one? If you have many configuration options in your `jest.config.js`, can you simplify it?
The simpler you can make it, the more likely we are to successfully verify and fix the bug.
- type: markdown
attributes:
value: |
:bangbang: &nbsp; Bug reports without a minimal reproduction will be rejected. :bangbang:
---
- type: input
id: version
attributes:
label: Version
description: |
The version of Jest you are using.
Is it the [latest](https://github.com/facebook/jest/releases)? Test and see if the bug has already been fixed.
placeholder: ex. 27.0.6
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: Steps to reproduce
description: Please link to a repository with a minimal reproduction and describe accurately how we can reproduce/verify the bug.
placeholder: |
Example steps (replace with your own):
1. Clone my repo at https://github.com/<myuser>/example
2. yarn install
3. yarn test
4. You should see the error come up
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
description: A description of what you expect to happen.
placeholder: I expect to see X or Y
validations:
required: true
- type: textarea
id: what-happened
attributes:
label: Actual behavior
description: A clear and concise description of the unexpected behavior.
placeholder: A bug happened!
validations:
required: true
- type: textarea
id: context
attributes:
label: Additional context
description: Anything else that might be relevant
validations:
required: false
- type: textarea
id: envinfo
attributes:
label: Environment
description: |
Please paste the output of running `npx envinfo --preset jest`.
This will be automatically formatted as a code block, so no need for backticks.
placeholder: |
System:
OS: Linux 5.10 Debian GNU/Linux 9 (stretch)
CPU: (8) arm64
Binaries:
Node: 14.17.0 - /usr/local/bin/node
Yarn: 1.22.5 - /usr/local/bin/yarn
npm: 6.14.13 - /usr/local/bin/npm
npmPackages:
jest: 27.0.6 => 27.0.6
render: shell
validations:
required: true

1
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1 @@
blank_issues_enabled: false

46
.github/ISSUE_TEMPLATE/feature.yml vendored Normal file
View File

@ -0,0 +1,46 @@
name: Feature Proposal 🚀
description: Submit a proposal for a new feature
title: '[Feature]: '
labels: [':rocket: Feature Request']
body:
- type: markdown
attributes:
value: |
### Thank you for taking the time to suggest a new feature!
- type: textarea
id: description
attributes:
label: '🚀 Feature Proposal'
description: A clear and concise description of what the feature is.
validations:
required: true
- type: textarea
id: solution
attributes:
label: Motivation
description: Outline your motivation for the proposal. How will it make Jest better?
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Example
description: Describe how this feature would be used.
validations:
required: false
- type: textarea
id: extra
attributes:
label: Pitch
description: |
Why does this feature belong in the [Jest core platform](https://www.youtube.com/watch?v=NtjyeojAOBs)?
validations:
required: true
- type: markdown
attributes:
value: |
Common proposals that do not typically make it to core:
- New matchers (see [jest-extended](https://github.com/jest-community/jest-extended))
- Changes to the default reporter (use custom reporters instead)
- Changes to node/jsdom test environments (use custom environments instead)

27
.github/ISSUE_TEMPLATE/question.yml vendored Normal file
View File

@ -0,0 +1,27 @@
name: 'Questions / Help 💬'
description: If you have questions, please check Reactiflux or StackOverflow
title: '[Please read the message below]'
labels: [':speech_balloon: Question']
body:
- type: markdown
attributes:
value: |
## Questions and Help 💬
This issue tracker is reserved for bug reports and feature proposals.
For anything else, such as questions or getting help, please see:
- [The Jest help page](https://jestjs.io/help)
- [Our `#testing` channel in Reactiflux](https://discord.gg/j6FKKQQrW9)
- The [jestjs](https://stackoverflow.com/questions/tagged/jestjs) tag on [StackOverflow](https://stackoverflow.com/questions/ask)
- type: checkboxes
id: no-post
attributes:
label: |
Please do not submit this issue.
description: |
:bangbang: &nbsp; This issue will be closed. :bangbang:
options:
- label: I understand
required: true

11
.github/PULL_REQUEST_TEMPLATE.md vendored Normal file
View File

@ -0,0 +1,11 @@
<!-- Thanks for submitting a pull request! Please provide enough information so that others can review your pull request. The two fields below are mandatory. -->
<!-- Please remember to update CHANGELOG.md at the root of the project if you have not done so. -->
## Summary
<!-- Explain the **motivation** for making this change. What existing problem does the pull request solve? -->
## Test plan
<!-- Demonstrate the code is solid. Example: The exact commands you ran and their output, screenshots / videos if the pull request changes UI. -->

3
.github/SUPPORT.md vendored Normal file
View File

@ -0,0 +1,3 @@
Please note this issue tracker is not a help forum. We recommend using [StackOverflow](https://stackoverflow.com/questions/tagged/jest) or [Reactiflux](https://discord.gg/j6FKKQQrW9) for questions.
<!-- Love Jest? Please consider supporting our collective: 👉 https://opencollective.com/jest/donate -->

6
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,6 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: daily

15
.github/workflows/issues.yml vendored Normal file
View File

@ -0,0 +1,15 @@
name: 'Close issues'
on:
issues:
types: [labeled]
jobs:
questions:
runs-on: ubuntu-latest
steps:
- name: Close Issue
uses: peter-evans/close-issue@v1
if: "${{ github.event.label.name == ':speech_balloon: Question' }}"
with:
comment: Please note this issue tracker is not a help forum. We recommend using [StackOverflow](https://stackoverflow.com/questions/tagged/jestjs) or our [discord channel](https://discord.gg/j6FKKQQrW9) for questions.

27
.github/workflows/lock.yml vendored Normal file
View File

@ -0,0 +1,27 @@
name: 'Lock Threads'
on:
schedule:
- cron: '0 0 * * *'
jobs:
lock:
runs-on: ubuntu-latest
steps:
- uses: dessant/lock-threads@v3
with:
github-token: ${{ github.token }}
issue-inactive-days: '30'
exclude-any-issue-labels: 'Discussion'
issue-comment: >
This issue has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using [StackOverflow](https://stackoverflow.com/questions/tagged/jestjs) or our [discord channel](https://discord.gg/j6FKKQQrW9) for questions.
pr-inactive-days: '30'
pr-comment: >
This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Please note this issue tracker is not a help forum. We recommend using [StackOverflow](https://stackoverflow.com/questions/tagged/jestjs) or our [discord channel](https://discord.gg/j6FKKQQrW9) for questions.

130
.github/workflows/nodejs.yml vendored Normal file
View File

@ -0,0 +1,130 @@
name: Node CI
on:
push:
branches:
- main
pull_request:
branches:
- '**'
jobs:
cleanup-runs:
runs-on: ubuntu-latest
steps:
- uses: rokroskar/workflow-run-cleanup-action@master
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/main'"
prepare-yarn-cache:
name: Prepare yarn cache
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: lts/*
cache: yarn
- name: Validate cache
env:
# Use PnP and disable postinstall scripts as this just needs to
# populate the cache for the other jobs
YARN_NODE_LINKER: pnp
YARN_ENABLE_SCRIPTS: false
run: yarn --immutable
lint-and-typecheck:
name: Running TypeScript compiler & ESLint
runs-on: ubuntu-latest
needs: prepare-yarn-cache
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
with:
node-version: lts/*
cache: yarn
- name: install
run: yarn --immutable
- name: build
run: yarn build
- name: test typings
run: yarn test-types
- name: verify TypeScript@3.8 compatibility
run: yarn verify-old-ts
- name: verify Yarn PnP compatibility
run: yarn verify-pnp
- name: run eslint
run: yarn lint
- name: run prettier
run: yarn lint:prettier:ci
- name: check copyright headers
run: yarn check-copyright-headers
test:
name: Node v${{ matrix.node-version }} on ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
node-version: [10.x, 12.x, 14.x, 15.x, 16.x, 17.x]
os: [ubuntu-latest, macOS-latest, windows-latest]
runs-on: ${{ matrix.os }}
needs: prepare-yarn-cache
steps:
- name: Set git config
shell: bash
run: |
git config --global core.autocrlf false
git config --global core.symlinks true
if: runner.os == 'Windows'
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v2
with:
node-version: ${{ matrix.node-version }}
cache: yarn
- name: install
run: yarn --immutable
- name: build
run: yarn build:js
- name: Get number of CPU cores
id: cpu-cores
uses: SimenB/github-actions-cpu-cores@v1
- name: run tests
run: yarn test-ci-partial:parallel --max-workers ${{ steps.cpu-cores.outputs.count }}
test-jasmine:
name: Node LTS on ${{ matrix.os }} using jest-jasmine2
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macOS-latest, windows-latest]
runs-on: ${{ matrix.os }}
needs: prepare-yarn-cache
steps:
- name: Set git config
shell: bash
run: |
git config --global core.autocrlf false
git config --global core.symlinks true
if: runner.os == 'Windows'
- uses: actions/checkout@v2
- name: Use Node.js LTS
uses: actions/setup-node@v2
with:
node-version: lts/*
cache: yarn
- name: install
run: yarn --immutable
- name: build
run: yarn build:js
- name: Get number of CPU cores
id: cpu-cores
uses: SimenB/github-actions-cpu-cores@v1
- name: run tests using jest-jasmine
run: yarn jest-jasmine-ci --max-workers ${{ steps.cpu-cores.outputs.count }}

55
.gitignore vendored Normal file
View File

@ -0,0 +1,55 @@
.idea
.DS_STORE
.eslintcache
*.swp
*~
/examples/*/node_modules/
/examples/mongodb/globalConfig.json
/e2e/*/node_modules
/e2e/*/.pnp
/e2e/*/.pnp.js
!/e2e/presets/json/node_modules
!/e2e/presets/js/node_modules
/e2e/transform/*/coverage
/e2e/transform/*/node_modules
/node_modules
/packages/*/build/
/packages/*/dist/
/packages/*/coverage/
/packages/*/node_modules/
/website/.docusaurus
/website/build
/website/backers.json
/website/node_modules
/website/yarn.lock
/website/i18n/*
/benchmarks/*/node_modules/
/reports/*
coverage
lerna-debug.log
npm-debug.log
npm-debug.log*
yarn-error.log*
junit.xml
package-lock.json
*.tsbuildinfo
api-extractor.json
# https://yarnpkg.com/getting-started/qa#which-files-should-be-gitignored but nested for e2e directories
**/.yarn/*
!**/.yarn/patches
!**/.yarn/releases
!**/.yarn/plugins
!**/.yarn/sdks
!**/.yarn/versions
**/.pnp.*
crowdin-cli.jar

5
.prettierignore Normal file
View File

@ -0,0 +1,5 @@
fixtures/failing-jsons/
packages/jest-diff/src/cleanupSemantic.ts
packages/jest-config/src/__tests__/jest-preset.json
packages/pretty-format/perf/world.geo.json
website/versions.json

1
.watchmanconfig Normal file
View File

@ -0,0 +1 @@
[]

6
.yarnrc Normal file
View File

@ -0,0 +1,6 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
lastUpdateCheck 1576854265612
yarn-path ".yarn/releases/yarn-sources.cjs"

19
.yarnrc.yml Normal file
View File

@ -0,0 +1,19 @@
enableGlobalCache: true
nodeLinker: node-modules
packageExtensions:
react-native@*:
peerDependencies:
"@babel/preset-env": "^7.1.6"
react-native-codegen@*:
peerDependencies:
"@babel/preset-env": "^7.1.6"
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
spec: "@yarnpkg/plugin-workspace-tools"
yarnPath: .yarn/releases/yarn-2.4.3.cjs

3030
CHANGELOG.md Normal file

File diff suppressed because it is too large Load Diff

47
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,47 @@
# Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful.
## Scope
This Code of Conduct applies within all project spaces, and it also applies when an individual is representing the project or its community in public spaces. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at <opensource-conduct@fb.com>. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see https://www.contributor-covenant.org/faq

238
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,238 @@
# How to Contribute
Jest is one of Facebook's open source projects that is both under very active development and is also being used to ship code to everybody on [facebook.com](https://www.facebook.com). We're still working out the kinks to make contributing to this project as easy and transparent as possible, but we're not quite there yet. Hopefully this document makes the process for contributing clear and answers some questions that you may have.
## [Code of Conduct](https://code.facebook.com/codeofconduct)
Facebook has adopted a Code of Conduct that we expect project participants to adhere to. Please read [the full text](https://code.facebook.com/codeofconduct) so that you can understand what actions will and will not be tolerated.
## Open Development
All work on Jest happens directly on [GitHub](/). Both core team members and external contributors send pull requests which go through the same review process.
### `main` is unsafe
We will do our best to keep `main` in good shape, with tests passing at all times. But in order to move fast, we will make API changes that your application might not be compatible with. We will do our best to communicate these changes and always version appropriately so you can lock into a specific version if need be.
### Workflow and Pull Requests
The core team will be monitoring for pull requests. When we get one, we'll run some Facebook-specific integration tests on it first. From here, we'll need to get another person to sign off on the changes and then merge the pull request. For API changes we may need to fix internal uses, which could cause some delay. We'll do our best to provide updates and feedback throughout the process.
_Before_ submitting a pull request, please make sure the following is done…
1. Fork the repo and create your branch from `main`. A guide on how to fork a repository: https://help.github.com/articles/fork-a-repo/
Open terminal (e.g. Terminal, iTerm, Git Bash or Git Shell) and type:
```sh-session
$ git clone https://github.com/<your_username>/jest
$ cd jest
$ git checkout -b my_branch
```
Note: Replace `<your_username>` with your GitHub username
1. Jest uses [Yarn](https://code.facebook.com/posts/1840075619545360) for running development scripts. If you haven't already done so, please [install yarn](https://yarnpkg.com/en/docs/install).
1. Make sure you have `python` installed. Python is required by [node-gyp](https://github.com/nodejs/node-gyp) that is used when running `yarn install`.
To check your version of Python and ensure it's installed you can type:
```sh
python --version
```
1. Make sure you have a compatible version of `node` installed (As of October 29th 2021, `v16.x` is recommended).
```sh
node -v
```
1. Run `yarn install`. On Windows: To install [Yarn](https://yarnpkg.com/en/docs/install#windows-tab) on Windows you may need to download either node.js or Chocolatey<br />
```sh
yarn install
```
To check your version of Yarn and ensure it's installed you can type:
```sh
yarn --version
```
On Windows `yarn install` may fail with `gyp ERR! build error`. One of possible solutions:
```sh
yarn global add windows-build-tools
```
1. Run `yarn build` to transpile TypeScript to JavaScript and type check the code
```sh
yarn build
```
1. If you've added code that should be tested, add tests. You can use watch mode that continuously transforms changed files to make your life easier.
```sh
# in the background
yarn watch
```
1. If you've changed APIs, update the documentation.
1. Ensure the test suite passes via `yarn jest`. To run the test suite you may need to install [Mercurial](https://www.mercurial-scm.org/) (`hg`). On macOS, this can be done using [homebrew](http://brew.sh/): `brew install hg`.
```sh-session
$ brew install hg # maybe
$ yarn test
```
1. If you haven't already, complete the [CLA](https://code.facebook.com/cla/).
#### Changelog entries
All changes that add a feature to or fix a bug in any of Jest's packages require a changelog entry containing the names of the packages affected, a description of the change, and the number of and link to the pull request. Try to match the structure of the existing entries.
For significant changes to the documentation or website and things like cleanup, refactoring, and dependency updates, the "Chore & Maintenance" section of the changelog can be used.
You can add or edit the changelog entry in the GitHub web interface once you have opened the pull request and know the number and link to it.
Make sure to alphabetically order your entry based on package name. If you have changed multiple packages, separate them with a comma.
#### Testing
Code that is written needs to be tested to ensure that it achieves the desired behaviour. Tests either fall into a unit test or an integration test.
##### Unit tests
Some of the packages within jest have a `__tests__` directory. This is where unit tests reside in. If the scope of your work only requires a unit test, this is where you will write it in. Tests here usually don't require much if any setup.
##### Integration tests
There will be situations however where the work you have done cannot be tested alone using unit tests. In situations like this, you should write an integration test for your code. The integration tests reside within the `e2e` directory. Within this directory, there is a `__tests__` directory. This is where you will write the integration test itself. The tests within this directory execute jest itself using `runJest.js` and assertions are usually made on one if not all the output of the following `status`, `stdout` and `stderr`. The other sub directories within the `e2e` directory are where you will write the files that jest will run for your integration tests. Feel free to take a look at any of the tests in the `__tests__` directory within `e2e` to have a better sense of how it is currently being done.
It is possible to run the integration test itself manually to inspect that the new behaviour is indeed correct. Here is a small code snippet of how to do just that. This is useful when debugging a failing test.
```bash
$ cd e2e/clear-cache
$ node ../../packages/jest-cli/bin/jest.js # It is possible to use node --inspect or ndb
PASS __tests__/clear_cache.test.js
✓ stub (3ms)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.232 s, estimated 1 s
Ran all test suites.
```
##### Using jest-circus
There may be cases where you want to run jest using `jest-circus` instead of `jest-jasmine2` (which is the default runner) for integration testing. In situations like this, set the environment variable `JEST_CIRCUS` to 1. That will configure jest to use `jest-circus`. So something like this.
```bash
JEST_CIRCUS=1 yarn jest
```
#### Additional Workflow for any changes made to website or docs
If you are making changes to the website or documentation, test the website folder and run the server to check if your changes are being displayed accurately.
1. Locate to the website directory and install any website specific dependencies by typing in `yarn`. Following steps are to be followed for this purpose from the root directory.
```sh-session
$ cd website # Only needed if you are not already in the website directory
$ yarn
$ node fetchSupporters.js
$ yarn start
```
1. You can run a development server to check if the changes you made are being displayed accurately by running `yarn start` in the website directory.
The Jest website also offers documentation for older versions of Jest, which you can edit in `website/versioned_docs`. After making changes to the current documentation in `docs`, please check if any older versions of the documentation have a copy of the file where the change is also relevant and apply the changes to the `versioned_docs` as well.
### Contributor License Agreement (CLA)
In order to accept your pull request, we need you to submit a CLA. You only need to do this once, so if you've done this for another Facebook open source project, you're good to go. If you are submitting a pull request for the first time, just let us know that you have completed the CLA and we can cross-check with your GitHub username.
[Complete your CLA here.](https://code.facebook.com/cla)
## How to try a development build of Jest in another project
To build Jest:
```sh-session
$ cd /path/to/your/Jest_clone
# Do one of the following:
# Check out a commit from another contributor, and then
$ yarn run build
# Or, save your changes to Jest, and then
$ yarn test # which also builds Jest
```
To run tests in another project with the development build of Jest:
```sh-session
$ cd /path/to/another/project
$ node /path/to/your/JestClone/packages/jest/bin/jest [options] # run jest-cli/bin/jest.js in the development build
```
- To decide whether to specify any options, see `test` under `scripts` in the `package.json` file of the other project.
## Bugs
### Where to Find Known Issues
We will be using GitHub Issues for our public bugs. We will keep a close eye on this and try to make it clear when we have an internal fix in progress. Before filing a new issue, try to make sure your problem doesn't already exist.
### Reporting New Issues
The best way to get your bug fixed is to provide a reduced test case. Please provide a public repository with a runnable example.
### Docs translation
We get translations from Crowdin, see https://crowdin.com/project/jest-v2. Any and all help is very much appreciated!
### Security Bugs
Facebook has a [bounty program](https://www.facebook.com/whitehat/) for the safe disclosure of security bugs. With that in mind, please do not file public issues; go through the process outlined on that page.
## How to Get in Touch
[`#testing` on Reactiflux](https://discord.gg/j6FKKQQrW9)
## Code Conventions
- 2 spaces for indentation (no tabs).
- 80 character line length strongly preferred.
- Prefer `'` over `"`.
- ES6 syntax when possible.
- Use [TypeScript](https://www.typescriptlang.org/).
- Use semicolons;
- Trailing commas,
- Avd abbr wrds.
## Credits
This project exists thanks to all the people who [contribute](CONTRIBUTING.md).
<a href="graphs/contributors"><img src="https://opencollective.com/jest/contributors.svg?width=890&button=false" /></a>
### [Backers](https://opencollective.com/jest#backer)
Thank you to all our backers! 🙏
<a href="https://opencollective.com/jest#backers" target="_blank"><img src="https://opencollective.com/jest/backers.svg?width=890"></a>
### [Sponsors](https://opencollective.com/jest#sponsor)
Support this project by becoming a sponsor. Your logo will show up here with a link to your website.
<a href="https://opencollective.com/jest/sponsor/0/website" target="_blank"><img src="https://opencollective.com/jest/sponsor/0/avatar.svg"></a> <a href="https://opencollective.com/jest/sponsor/1/website" target="_blank"><img src="https://opencollective.com/jest/sponsor/1/avatar.svg"></a> <a href="https://opencollective.com/jest/sponsor/2/website" target="_blank"><img src="https://opencollective.com/jest/sponsor/2/avatar.svg"></a> <a href="https://opencollective.com/jest/sponsor/3/website" target="_blank"><img src="https://opencollective.com/jest/sponsor/3/avatar.svg"></a> <a href="https://opencollective.com/jest/sponsor/4/website" target="_blank"><img src="https://opencollective.com/jest/sponsor/4/avatar.svg"></a> <a href="https://opencollective.com/jest/sponsor/5/website" target="_blank"><img src="https://opencollective.com/jest/sponsor/5/avatar.svg"></a> <a href="https://opencollective.com/jest/sponsor/6/website" target="_blank"><img src="https://opencollective.com/jest/sponsor/6/avatar.svg"></a> <a href="https://opencollective.com/jest/sponsor/7/website" target="_blank"><img src="https://opencollective.com/jest/sponsor/7/avatar.svg"></a> <a href="https://opencollective.com/jest/sponsor/8/website" target="_blank"><img src="https://opencollective.com/jest/sponsor/8/avatar.svg"></a> <a href="https://opencollective.com/jest/sponsor/9/website" target="_blank"><img src="https://opencollective.com/jest/sponsor/9/avatar.svg"></a>
## License
By contributing to Jest, you agree that your contributions will be licensed under its MIT license.

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) Facebook, Inc. and its 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.

264
README.md Normal file
View File

@ -0,0 +1,264 @@
<p align="center">
<a href="https://badge.fury.io/js/jest">
<img src="https://badge.fury.io/js/jest.svg" alt="npm version">
</a>
<a href="https://github.com/facebook/jest/blob/main/LICENSE">
<img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="Jest is released under the MIT license." />
</a>
<a href="https://twitter.com/intent/follow?screen_name=fbjest">
<img src="https://img.shields.io/twitter/follow/fbjest.svg?style=social&label=Follow%20@fbjest" alt="Follow on Twitter" />
</a>
</p>
<!-- A spacer -->
<p>&nbsp;</p>
<p align="center"><img src="website/static/img/jest-readme-headline.png" width="80%"/></p>
<h2 align="center">🃏 Delightful JavaScript Testing</h2>
**👩🏻‍💻 Developer Ready**: A comprehensive JavaScript testing solution. Works out of the box for most JavaScript projects.
**🏃🏽 Instant Feedback**: Fast, interactive watch mode only runs test files related to changed files.
**📸 Snapshot Testing**: Capture snapshots of large objects to simplify testing and to analyze how they change over time.
<p align="right"><em>See more on <a href="https://jestjs.io">jestjs.io</a></em></p>
## Table of Contents
- [Getting Started](#getting-started)
- [Running from command line](#running-from-command-line)
- [Additional Configuration](#additional-configuration)
- [Generate a basic configuration file](#generate-a-basic-configuration-file)
- [Using Babel](#using-babel)
- [Using Webpack](#using-webpack)
- [Using Parcel](#using-parcel)
- [Using Typescript](#using-typescript)
- [Documentation](#documentation)
- [Badge](#badge)
- [Contributing](#contributing)
- [Code of Conduct](#code-of-conduct)
- [Contributing Guide](#contributing-guide)
- [Good First Issues](#good-first-issues)
- [Credits](#credits)
- [Backers](#backers)
- [Sponsors](#sponsors)
- [License](#license)
## Getting Started
<!-- copied from Getting Started docs, links updated to point to Jest website -->
Install Jest using [`yarn`](https://yarnpkg.com/en/package/jest):
```bash
yarn add --dev jest
```
Or [`npm`](https://www.npmjs.com/package/jest):
```bash
npm install --save-dev jest
```
Note: Jest documentation uses `yarn` commands, but `npm` will also work. You can compare `yarn` and `npm` commands in the [yarn docs, here](https://yarnpkg.com/en/docs/migrating-from-npm#toc-cli-commands-comparison).
Let's get started by writing a test for a hypothetical function that adds two numbers. First, create a `sum.js` file:
```javascript
function sum(a, b) {
return a + b;
}
module.exports = sum;
```
Then, create a file named `sum.test.js`. This will contain our actual test:
```javascript
const sum = require('./sum');
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
```
Add the following section to your `package.json`:
```json
{
"scripts": {
"test": "jest"
}
}
```
Finally, run `yarn test` or `npm test` and Jest will print this message:
```bash
PASS ./sum.test.js
✓ adds 1 + 2 to equal 3 (5ms)
```
**You just successfully wrote your first test using Jest!**
This test used `expect` and `toBe` to test that two values were exactly identical. To learn about the other things that Jest can test, see [Using Matchers](https://jestjs.io/docs/using-matchers).
## Running from command line
You can run Jest directly from the CLI (if it's globally available in your `PATH`, e.g. by `yarn global add jest` or `npm install jest --global`) with a variety of useful options.
Here's how to run Jest on files matching `my-test`, using `config.json` as a configuration file and display a native OS notification after the run:
```bash
jest my-test --notify --config=config.json
```
If you'd like to learn more about running `jest` through the command line, take a look at the [Jest CLI Options](https://jestjs.io/docs/cli) page.
## Additional Configuration
### Generate a basic configuration file
Based on your project, Jest will ask you a few questions and will create a basic configuration file with a short description for each option:
```bash
jest --init
```
### Using Babel
To use [Babel](https://babeljs.io/), install required dependencies via `yarn`:
```bash
yarn add --dev babel-jest @babel/core @babel/preset-env
```
Configure Babel to target your current version of Node by creating a `babel.config.js` file in the root of your project:
```javascript
// babel.config.js
module.exports = {
presets: [['@babel/preset-env', {targets: {node: 'current'}}]],
};
```
_The ideal configuration for Babel will depend on your project._ See [Babel's docs](https://babeljs.io/docs/en/) for more details.
<details><summary markdown="span"><strong>Making your Babel config jest-aware</strong></summary>
Jest will set `process.env.NODE_ENV` to `'test'` if it's not set to something else. You can use that in your configuration to conditionally setup only the compilation needed for Jest, e.g.
```javascript
// babel.config.js
module.exports = api => {
const isTest = api.env('test');
// You can use isTest to determine what presets and plugins to use.
return {
// ...
};
};
```
> Note: `babel-jest` is automatically installed when installing Jest and will automatically transform files if a babel configuration exists in your project. To avoid this behavior, you can explicitly reset the `transform` configuration option:
```javascript
// jest.config.js
module.exports = {
transform: {},
};
```
</details>
<!-- Note that the Babel 6 section in the Getting Started was removed -->
### Using webpack
Jest can be used in projects that use [webpack](https://webpack.js.org/) to manage assets, styles, and compilation. webpack does offer some unique challenges over other tools. Refer to the [webpack guide](https://jestjs.io/docs/webpack) to get started.
### Using parcel
Jest can be used in projects that use [parcel-bundler](https://parceljs.org/) to manage assets, styles, and compilation similar to webpack. Parcel requires zero configuration. Refer to the official [docs](https://parceljs.org/docs/) to get started.
### Using TypeScript
Jest supports TypeScript, via Babel. First, make sure you followed the instructions on [using Babel](#using-babel) above. Next, install the `@babel/preset-typescript` via `yarn`:
```bash
yarn add --dev @babel/preset-typescript
```
Then add `@babel/preset-typescript` to the list of presets in your `babel.config.js`.
```diff
// babel.config.js
module.exports = {
presets: [
['@babel/preset-env', {targets: {node: 'current'}}],
+ '@babel/preset-typescript',
],
};
```
However, there are some [caveats](https://babeljs.io/docs/en/babel-plugin-transform-typescript#caveats) to using TypeScript with Babel. Because TypeScript support in Babel is purely transpilation, Jest will not type-check your tests as they are run. If you want that, you can use [ts-jest](https://github.com/kulshekhar/ts-jest) instead, or just run the TypeScript compiler [tsc](https://www.typescriptlang.org/docs/handbook/compiler-options.html) separately (or as part of your build process).
<!-- end copied -->
## Documentation
Learn more about using [Jest on the official site!](https://jestjs.io)
- [Getting Started](https://jestjs.io/docs/getting-started)
- [Guides](https://jestjs.io/docs/snapshot-testing)
- [API Reference](https://jestjs.io/docs/api)
- [Configuring Jest](https://jestjs.io/docs/configuration)
## Badge
Show the world you're using _Jest_ `→` [![tested with jest](https://img.shields.io/badge/tested_with-jest-99424f.svg)](https://github.com/facebook/jest) [![jest](https://jestjs.io/img/jest-badge.svg)](https://github.com/facebook/jest)
<!-- prettier-ignore -->
```md
[![tested with jest](https://img.shields.io/badge/tested_with-jest-99424f.svg)](https://github.com/facebook/jest)
[![jest](https://jestjs.io/img/jest-badge.svg)](https://github.com/facebook/jest)
```
## Contributing
Development of Jest happens in the open on GitHub, and we are grateful to the community for contributing bugfixes and improvements. Read below to learn how you can take part in improving Jest.
### [Code of Conduct](https://code.facebook.com/codeofconduct)
Facebook has adopted a Code of Conduct that we expect project participants to adhere to. Please read [the full text](https://code.facebook.com/codeofconduct) so that you can understand what actions will and will not be tolerated.
### [Contributing Guide](CONTRIBUTING.md)
Read our [contributing guide](CONTRIBUTING.md) to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes to Jest.
### [Good First Issues](https://github.com/facebook/jest/labels/good%20first%20issue)
To help you get your feet wet and get you familiar with our contribution process, we have a list of [good first issues](https://github.com/facebook/jest/labels/good%20first%20issue) that contain bugs which have a relatively limited scope. This is a great place to get started.
## Credits
This project exists thanks to all the people who [contribute](CONTRIBUTING.md).
<a href="https://github.com/facebook/jest/graphs/contributors"><img src="https://opencollective.com/jest/contributors.svg?width=890&button=false" /></a>
### [Backers](https://opencollective.com/jest#backer)
Thank you to all our backers! 🙏
<a href="https://opencollective.com/jest#backers" target="_blank"><img src="https://opencollective.com/jest/backers.svg?width=890"></a>
### [Sponsors](https://opencollective.com/jest#sponsor)
Support this project by becoming a sponsor. Your logo will show up here with a link to your website.
<a href="https://opencollective.com/jest/sponsor/0/website" target="_blank"><img src="https://opencollective.com/jest/sponsor/0/avatar.svg"></a> <a href="https://opencollective.com/jest/sponsor/1/website" target="_blank"><img src="https://opencollective.com/jest/sponsor/1/avatar.svg"></a> <a href="https://opencollective.com/jest/sponsor/2/website" target="_blank"><img src="https://opencollective.com/jest/sponsor/2/avatar.svg"></a> <a href="https://opencollective.com/jest/sponsor/3/website" target="_blank"><img src="https://opencollective.com/jest/sponsor/3/avatar.svg"></a> <a href="https://opencollective.com/jest/sponsor/4/website" target="_blank"><img src="https://opencollective.com/jest/sponsor/4/avatar.svg"></a> <a href="https://opencollective.com/jest/sponsor/5/website" target="_blank"><img src="https://opencollective.com/jest/sponsor/5/avatar.svg"></a> <a href="https://opencollective.com/jest/sponsor/6/website" target="_blank"><img src="https://opencollective.com/jest/sponsor/6/avatar.svg"></a> <a href="https://opencollective.com/jest/sponsor/7/website" target="_blank"><img src="https://opencollective.com/jest/sponsor/7/avatar.svg"></a> <a href="https://opencollective.com/jest/sponsor/8/website" target="_blank"><img src="https://opencollective.com/jest/sponsor/8/avatar.svg"></a> <a href="https://opencollective.com/jest/sponsor/9/website" target="_blank"><img src="https://opencollective.com/jest/sponsor/9/avatar.svg"></a>
## License
Jest is [MIT licensed](./LICENSE).

View File

@ -0,0 +1,12 @@
root = true
[*]
indent_style = tab
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.yml]
indent_style = space
indent_size = 2

1
astral-regex/.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
* text=auto eol=lf

2
astral-regex/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
node_modules
yarn.lock

1
astral-regex/.npmrc Normal file
View File

@ -0,0 +1 @@
package-lock=false

4
astral-regex/.travis.yml Normal file
View File

@ -0,0 +1,4 @@
language: node_js
node_js:
- '10'
- '8'

28
astral-regex/index.d.ts vendored Normal file
View File

@ -0,0 +1,28 @@
declare namespace astralRegex {
interface Options {
/**
Only match an exact string. Useful with `RegExp#test()` to check if a string is a astral symbol. Default: `false` _(Matches any astral symbols in a string)_
*/
readonly exact?: boolean;
}
}
/**
Regular expression for matching [astral symbols](https://everything2.com/title/astral+plane).
@returns A `RegExp` for matching astral symbols.
@example
```
import astralRegex = require('astral-regex');
astralRegex({exact: true}).test('🦄');
//=> true
'foo 🦄 💩 bar'.match(astralRegex());
//=> ['🦄', '💩']
```
*/
declare function astralRegex(options?: astralRegex.Options): RegExp;
export = astralRegex;

6
astral-regex/index.js Normal file
View File

@ -0,0 +1,6 @@
'use strict';
const regex = '[\uD800-\uDBFF][\uDC00-\uDFFF]';
const astralRegex = options => options && options.exact ? new RegExp(`^${regex}$`) : new RegExp(regex, 'g');
module.exports = astralRegex;

View File

@ -0,0 +1,5 @@
import {expectType} from 'tsd';
import astralRegex = require('.');
expectType<RegExp>(astralRegex());
expectType<RegExp>(astralRegex({exact: true}));

9
astral-regex/license Normal file
View File

@ -0,0 +1,9 @@
MIT License
Copyright (c) Kevin Mårtensson <kevinmartensson@gmail.com> (github.com/kevva)
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.

33
astral-regex/package.json Normal file
View File

@ -0,0 +1,33 @@
{
"name": "astral-regex",
"version": "2.0.0",
"description": "Regular expression for matching astral symbols",
"license": "MIT",
"repository": "kevva/astral-regex",
"author": {
"name": "Kevin Mårtensson",
"email": "kevinmartensson@gmail.com",
"url": "github.com/kevva"
},
"engines": {
"node": ">=8"
},
"scripts": {
"test": "xo && ava && tsd"
},
"files": [
"index.js",
"index.d.ts"
],
"keywords": [
"astral",
"emoji",
"regex",
"surrogate"
],
"devDependencies": {
"ava": "^1.4.1",
"tsd": "^0.7.2",
"xo": "^0.24.0"
}
}

46
astral-regex/readme.md Normal file
View File

@ -0,0 +1,46 @@
# astral-regex [![Build Status](https://travis-ci.org/kevva/astral-regex.svg?branch=master)](https://travis-ci.org/kevva/astral-regex)
> Regular expression for matching [astral symbols](https://everything2.com/title/astral+plane)
## Install
```
$ npm install astral-regex
```
## Usage
```js
const astralRegex = require('astral-regex');
astralRegex({exact: true}).test('🦄');
//=> true
'foo 🦄 💩 bar'.match(astralRegex());
//=> ['🦄', '💩']
```
## API
### astralRegex([options])
Returns a `RegExp` for matching astral symbols.
#### options
Type: `Object`
##### exact
Type: `boolean`<br>
Default: `false` *(Matches any astral symbols in a string)*
Only match an exact string. Useful with `RegExp#test()` to check if a string is a astral symbol.
## License
MIT © [Kevin Mårtensson](https://github.com/kevva)

37
astral-regex/test.js Normal file
View File

@ -0,0 +1,37 @@
import test from 'ava';
import astralRegex from '.';
const matches = [
'💩',
'🦄',
'🎠',
'🌈',
'🐴',
'😹'
];
const nonMatches = [
'a',
'안',
'1',
'Z͑ͫ̓ͪ̂ͫ̽͏̴̙̤̞͉͚̯̞̠͍A̴̵̜̰͔ͫ͗͢L̠ͨͧͩ͘G̴̻͈͍͔̹̑͗̎̅͛́Ǫ̵̹̻̝̳͂̌̌͘!͖̬̰̙̗̿̋ͥͥ̂ͣ̐́́͜͞',
'…',
'π',
'®'
];
test('matches', t => {
for (const x of matches) {
t.true(astralRegex({exact: true}).test(x));
}
for (const x of matches) {
t.is((astralRegex().exec(`foo ${x} bar`) || [])[0], x);
}
});
test('non matches', t => {
for (const x of nonMatches) {
t.false(astralRegex({exact: true}).test(x));
}
});

View File

@ -0,0 +1,227 @@
# Change Log
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
<a name="3.2.0"></a>
# [3.2.0](https://github.com/moxystudio/babel-preset-moxy/compare/v3.1.0...v3.2.0) (2019-09-30)
### Bug Fixes
* update vulnerable/outdated dependencies ([9f44a8d](https://github.com/moxystudio/babel-preset-moxy/commit/9f44a8d))
### Features
* add nullish coalescing and optional chaining operators ([25faa38](https://github.com/moxystudio/babel-preset-moxy/commit/25faa38))
<a name="3.1.0"></a>
# [3.1.0](https://github.com/moxystudio/babel-preset-moxy/compare/v3.0.6...v3.1.0) (2019-05-28)
### Features
* enable loose option ([#44](https://github.com/moxystudio/babel-preset-moxy/issues/44)) ([9b4afe1](https://github.com/moxystudio/babel-preset-moxy/commit/9b4afe1))
<a name="3.0.6"></a>
## [3.0.6](https://github.com/moxystudio/babel-preset-moxy/compare/v3.0.5...v3.0.6) (2019-04-29)
### Bug Fixes
* add sourceType also to lib ([4b1cf95](https://github.com/moxystudio/babel-preset-moxy/commit/4b1cf95))
* set sourceType to unambiguous to properly compile deps ([b32f8c6](https://github.com/moxystudio/babel-preset-moxy/commit/b32f8c6))
<a name="3.0.5"></a>
## [3.0.5](https://github.com/moxystudio/babel-preset-moxy/compare/v3.0.4...v3.0.5) (2019-04-04)
### Bug Fixes
* add the runtime transform plugin to end project ([#42](https://github.com/moxystudio/babel-preset-moxy/issues/42)) ([8127ae7](https://github.com/moxystudio/babel-preset-moxy/commit/8127ae7))
<a name="3.0.4"></a>
## [3.0.4](https://github.com/moxystudio/babel-preset-moxy/compare/v3.0.3...v3.0.4) (2019-02-14)
### Bug Fixes
* add lib directory and update readme without preset shorthand ([#40](https://github.com/moxystudio/babel-preset-moxy/issues/40)) ([6ee6573](https://github.com/moxystudio/babel-preset-moxy/commit/6ee6573))
<a name="3.0.3"></a>
## [3.0.3](https://github.com/moxystudio/babel-preset-moxy/compare/v3.0.2...v3.0.3) (2019-02-13)
<a name="3.0.2"></a>
## [3.0.2](https://github.com/moxystudio/babel-preset-moxy/compare/v3.0.1...v3.0.2) (2019-02-13)
<a name="3.0.1"></a>
## [3.0.1](https://github.com/moxystudio/babel-preset-moxy/compare/v3.0.0...v3.0.1) (2019-02-13)
<a name="3.0.0"></a>
# [3.0.0](https://github.com/moxystudio/babel-preset-moxy/compare/v2.3.5...v3.0.0) (2019-02-13)
### Features
* upgrade to babel 7 ([#39](https://github.com/moxystudio/babel-preset-moxy/issues/39)) ([5ad75f1](https://github.com/moxystudio/babel-preset-moxy/commit/5ad75f1)), closes [#26](https://github.com/moxystudio/babel-preset-moxy/issues/26)
### BREAKING CHANGES
* the way the preset is configured has changed
<a name="2.3.5"></a>
## [2.3.5](https://github.com/moxystudio/babel-preset-moxy/compare/v2.3.4...v2.3.5) (2018-09-11)
<a name="2.3.4"></a>
## [2.3.4](https://github.com/moxystudio/babel-preset-moxy/compare/v2.3.3...v2.3.4) (2018-09-10)
### Bug Fixes
* fix dev errors when using transform-react-inline-elements ([722526c](https://github.com/moxystudio/babel-preset-moxy/commit/722526c)), closes [#26](https://github.com/moxystudio/babel-preset-moxy/issues/26)
<a name="2.3.3"></a>
## [2.3.3](https://github.com/moxystudio/babel-preset-moxy/compare/v2.3.2...v2.3.3) (2018-03-22)
### Bug Fixes
* remove babel-plugin-transform-react-constant-elements ([#25](https://github.com/moxystudio/babel-preset-moxy/issues/25)) ([4c1ab03](https://github.com/moxystudio/babel-preset-moxy/commit/4c1ab03))
<a name="2.3.2"></a>
## [2.3.2](https://github.com/moxystudio/babel-preset-moxy/compare/v2.3.1...v2.3.2) (2018-03-11)
<a name="2.3.1"></a>
## [2.3.1](https://github.com/moxystudio/babel-preset-moxy/compare/v2.3.0...v2.3.1) (2018-03-02)
<a name="2.3.0"></a>
# [2.3.0](https://github.com/moxystudio/babel-preset-moxy/compare/v2.2.1...v2.3.0) (2018-02-28)
### Features
* add default support for async/await and generators ([96f8ce9](https://github.com/moxystudio/babel-preset-moxy/commit/96f8ce9))
<a name="2.2.1"></a>
## [2.2.1](https://github.com/moxystudio/babel-preset-moxy/compare/v2.2.0...v2.2.1) (2018-02-23)
### Bug Fixes
* fix lodash plugin being wrongly setup ([678a7c0](https://github.com/moxystudio/babel-preset-moxy/commit/678a7c0))
<a name="2.2.0"></a>
# [2.2.0](https://github.com/moxystudio/babel-preset-moxy/compare/v2.1.0...v2.2.0) (2018-02-20)
### Features
* add lodash/fp as default id on babel-plugin-lodash ([2c30efe](https://github.com/moxystudio/babel-preset-moxy/commit/2c30efe)), closes [#18](https://github.com/moxystudio/babel-preset-moxy/issues/18)
<a name="2.1.0"></a>
# [2.1.0](https://github.com/moxystudio/babel-preset-moxy/compare/v2.0.4...v2.1.0) (2018-01-24)
### Features
* add babel-plugin-lodash via options.lodash ([1104f92](https://github.com/moxystudio/babel-preset-moxy/commit/1104f92)), closes [#10](https://github.com/moxystudio/babel-preset-moxy/issues/10)
<a name="2.0.4"></a>
## [2.0.4](https://github.com/moxystudio/babel-preset-moxy/compare/v2.0.3...v2.0.4) (2018-01-22)
<a name="2.0.3"></a>
## [2.0.3](https://github.com/moxystudio/babel-preset-moxy/compare/v2.0.2...v2.0.3) (2017-12-21)
<a name="2.0.2"></a>
## [2.0.2](https://github.com/moxystudio/babel-preset-moxy/compare/v2.0.1...v2.0.2) (2017-11-23)
### Bug Fixes
* remove core-js peer dependency ([d8d3316](https://github.com/moxystudio/babel-preset-moxy/commit/d8d3316))
<a name="2.0.1"></a>
## [2.0.1](https://github.com/moxystudio/babel-preset-moxy/compare/v2.0.0...v2.0.1) (2017-11-23)
### Bug Fixes
* fix add-module-exports plugin not being in deps ([1535d86](https://github.com/moxystudio/babel-preset-moxy/commit/1535d86))
<a name="2.0.0"></a>
# [2.0.0](https://github.com/moxystudio/babel-preset-moxy/compare/v1.1.0...v2.0.0) (2017-11-23)
### Features
* implement namedDefaultExport ([f353508](https://github.com/moxystudio/babel-preset-moxy/commit/f353508)), closes [#5](https://github.com/moxystudio/babel-preset-moxy/issues/5)
### BREAKING CHANGES
* browser target was changed to be browsers for consistency with babel-preset-env
<a name="1.1.0"></a>
# [1.1.0](https://github.com/moxystudio/babel-preset-moxy/compare/v1.0.0...v1.1.0) (2017-11-22)
### Features
* improve modules option default value by reading BABEL_ENV ([276845b](https://github.com/moxystudio/babel-preset-moxy/commit/276845b))
<a name="1.0.0"></a>
# 1.0.0 (2017-11-20)
### Features
* initial commit ([79a708d](https://github.com/moxystudio/babel-preset-moxy/commit/79a708d))

21
babel-preset-moxy/LICENSE Normal file
View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2017 Made With MOXY Lda <hello@moxy.studio>
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.

272
babel-preset-moxy/README.md Normal file
View File

@ -0,0 +1,272 @@
# babel-preset-moxy
[![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Coverage Status][codecov-image]][codecov-url] [![Dependency status][david-dm-image]][david-dm-url] [![Dev Dependency status][david-dm-dev-image]][david-dm-dev-url]
[npm-url]:https://npmjs.org/package/babel-preset-moxy
[npm-image]:http://img.shields.io/npm/v/babel-preset-moxy.svg
[downloads-image]:http://img.shields.io/npm/dm/babel-preset-moxy.svg
[travis-url]:https://travis-ci.org/moxystudio/babel-preset-moxy
[travis-image]:http://img.shields.io/travis/moxystudio/babel-preset-moxy/master.svg
[codecov-url]:https://codecov.io/gh/moxystudio/babel-preset-moxy
[codecov-image]:https://img.shields.io/codecov/c/github/moxystudio/babel-preset-moxy/master.svg
[david-dm-url]:https://david-dm.org/moxystudio/babel-preset-moxy
[david-dm-image]:https://img.shields.io/david/moxystudio/babel-preset-moxy.svg
[david-dm-dev-url]:https://david-dm.org/moxystudio/babel-preset-moxy?type=dev
[david-dm-dev-image]:https://img.shields.io/david/dev/moxystudio/babel-preset-moxy.svg
[Babel](https://babeljs.io/) preset to be used at MOXY.
## Installation
```ssh
$ npm install babel-preset-moxy @babel/core --save-dev
```
If you are using Jest for testing, you also need to install [`babel-jest`](https://github.com/facebook/jest/tree/master/packages/babel-jest):
```ssh
$ npm install babel-jest --save-dev
```
## Motivation
Projects developed at MOXY often use new JavaScript language features that may not be supported in the targets they will run. This preset provides a shareable Babel config that:
- Allows you to use the latest JavaScript features and transpile only what is not already supported by your targets, thanks to [`preset-env`](https://www.npmjs.com/package/babel-preset-env)
- Enables [`class-properties`](https://www.npmjs.com/package/@babel/plugin-proposal-class-properties)
- Optionally enables React, transforming JSX to standard JavaScript
- Uses [`add-module-exports`](https://www.npmjs.com/package/babel-plugin-add-module-exports) to get around [babel#2212](https://github.com/babel/babel/issues/2212)
- Enables [`babel-plugin-lodash`](https://www.npmjs.com/package/babel-plugin-lodash)
## Do I need to transpile?
There has been [discussion](https://github.com/parcel-bundler/parcel/pull/559#discussion_r161926651) in the community about libraries not being compiled, leaving all compilation efforts to top-level projects consuming them. This makes sense, since developers know what platforms their top-level project target and are able to compile their dependencies accordingly. Furthermore, library maintainers are constantly having to update their compilation options as new features are accepted into different stages of the specification, which creates significant and unnecessary overhead.
Problems arise, however, in libraries which target both Node.js and browser, or if non-standard JavaScript is being used, such as [proposals](https://github.com/tc39/proposals) or [JSX](https://reactjs.org/docs/introducing-jsx.html). In those situations, library authors are required to transpile their libraries code to offer `CommonJS` and `ES` module variants or to transform non-standard JavaScript to standard JavaScript.
**In conclusion**:
1. For libraries, you need to transpile if you want to publish both in CommonJS and ES or if there are non-standard JavaScript language features being used
2. For top-level projects, you need to transpile both your code and your dependencies if the JavaScript language features being used are not supported by your targets
## Usage
### 1. Choose a preset-type
There're two preset types available for you to use:
- For libraries: use the `lib` type in case you are developing a library to be consumed by others
- For end-projects: use the `end-project` type in case you developing a top-level project, such as an Web Application, a Node.js API or CLI
### 2. Setup babel within your project
The way Babel is configured depends on the the tooling you are using. Below, there are instructions for common scenarios:
#### Standard project
> If you don't use a bundler within your project, this is the setup guide you should follow
- Create `.babelrc` at the root of your project, replacing `<preset-type>` with the preset type you chose:
```json
{
"presets": ["babel-preset-moxy/<preset-type>"]
}
```
...or with options:
```json
{
"presets": [["babel-preset-moxy/<preset-type>", { "react": true }]]
}
```
- Install [`@babel/cli`](https://www.npmjs.com/package/@babel/cli) as a dev dependency because we will need it for the build script:
```ssh
$ npm install @babel/cli --save-dev
```
- Set up your `package.json` like this:
```json
"main": "lib/index.js",
"module": "es/index.js",
"files": [
"lib",
"es"
],
"scripts": {
"build:commonjs": "BABEL_ENV=commonjs babel src -d lib --delete-dir-on-start",
"build:es": "BABEL_ENV=es babel src -d es --delete-dir-on-start",
"build": "npm run build:commonjs && npm run build:es"
}
```
Note that the build process above will produce both CommonJS and ES builds. If you just want to produce a single build, the `package.json` may be simplified. For example, to produce a single CommonJS build:
```json
"main": "lib/index.js",
"files": [
"lib"
],
"scripts": {
"build": "BABEL_ENV=es babel src -d es"
}
```
- Tweak your `.gitignore` file:
Add `lib/` and/or `es/` folder to the gitignore file so that those output folders are never committed.
- Create `src/index.js` and happy coding!
#### Webpack based project
Tweak your Webpack config JavaScript rule to include [`babel-loader`](https://www.npmjs.com/package/babel-loader) and MOXY's preset. Here's an example for a website project using React:
```js
{
test: /\.js$/,
use: [
{
loader: require.resolve('babel-loader'),
options: {
cacheDirectory: true, // Improve performance
presets: [
[require.resolve('babel-preset-moxy/end-proect'), {
targets: ['browsers'],
react: true,
modules: false,
}],
],
},
},
],
}
```
It's important that you do not exclude the `node_modules` folder so that everything goes through the `@babel/preset-env`, ensuring that all the produced code was transpiled according to the targets.
### 3. Tweak the options
Below, you may find a list containing all options you may tweak:
| Name | Description | Type | Default | in `lib` | in `end-project` |
| ------ | ------------- | -------- | ------- | ------------ | ------------ |
| react | Adds support for [React](https://reactjs.org/) | boolean | false | ✅ | ✅ |
| lodash | Transform to cherry-pick Lodash modules | boolean/[Object](https://github.com/lodash/babel-plugin-lodash#usage) | true | ✅ | ✅ |
| modules | Transform ES6 module syntax to another module type | [string/boolean](https://www.npmjs.com/package/babel-preset-env#modules) | Based on `process.env.BABEL_ENV`, `commonjs` if unspecified | ✅ | ✅ |
| dynamicImport | Adds support for `import()` statements | boolean | true | ✅ | ✅ |
| loose | Enable "loose" transformations for any plugins that allow them | boolean | true | ❌ | ✅ |
| targets | The output targets, see bellow for a more detailed explanation | Array/[Object](https://babeljs.io/docs/en/next/babel-preset-env.html#targets) | ['browsers', 'node'] | ❌ | ✅ |
| env | The environment (`development`, `production` or `test`) | string | Based on `process.env.NODE_ENV` | ❌ | ✅ |
| namedDefaultExport | Use [add-module-exports](https://github.com/59naga/babel-plugin-add-module-exports) plugin to get around [babel/babel#2212](https://github.com/babel/babel/issues/2212) | boolean | true if modules is `commonjs` | ✅ | ❌ |
#### `lodash` option
Specify which modules will have the cherry-pick transformation applied.
Note that `lodash-es`, `lodash-compat` and `lodash/fp` are always added for you, regardless of having this option defined or not.
For instance, to have smaller bundles when using [recompose](https://github.com/acdlite/recompose):
```json
{
"presets": [
["babel-preset-moxy/<preset-type>", {
"lodash": { "id": ["recompose"] }
}],
],
}
```
#### `targets` option
The targets option has a very important role. By default, its value is `['browsers', 'node']` which means that the compiled code will work in both the Browser and in Node.js.
When `browsers` is specified, the compiled code will work on browsers that are supported by [Google's browser support policy](https://github.com/awkaiser/browserslist-config-google). When `node` is specified, the compiled code will work on the last LTS or higher (currently `v8.9`).
If your project has different requirements in terms of browser or node support, you may specify the [targets](https://www.npmjs.com/package/babel-preset-env#targets) yourself as an object.
#### `dynamicImport` option
Dynamic imports support are enabled by default but are dependent on the `modules` option. More specifically, the [`syntax-dynamic-import`](https://www.npmjs.com/package/@babel/plugin-syntax-dynamic-import) and [`dynamic-import-node`](https://www.npmjs.com/package/babel-plugin-transform-dynamic-import) when the `modules` option is set to `false` and `commonjs` respectively.
For other `modules` types, such as `amd`, you must find and include a plugin yourself. Also, you may disable the `dynamicImport` option by setting it to `false` in case you want to disable the feature completely or if you want to choose another plugin.
#### `env` option
The `env`'s default value respects `process.env.NODE_ENV` and falls back to `production` if none are set. When env is `production`, some plugins that perform code optimization will be enabled.
The `modules` default value is `commonjs` unless `process.env.BABEL_ENV` is set to `es`.
### 4. Be aware of the caveats
No, seriously. Read the [Caveats](#caveats) section as it contains crucial information and might require you to do a few more steps.
## Caveats
### Polyfills
#### In libraries
Shipping polyfills in libraries is, in general, a bad practice because it increases the overall file size of your top-level project due to duplication.
The [`transform-runtime`](https://www.npmjs.com/package/babel-plugin-transform-runtime) plugin attempts to solve the polyfills and duplication by transforming `Object.assign`, `Promise` and other features to their [`core-js`](https://github.com/zloirock/core-js) counter-parts. Though, this doesn't play well with [`preset-env`](https://github.com/babel/babel-preset-env/tree/1.x/) because it inlines everything, including features that are already supported by our targets. Additionally, if different versions of the runtime are installed, duplication still happens.
For this reason, you, as an author, should state in the README of your library that you expect the environment to be polyfilled with [`core-js`](https://github.com/zloirock/core-js), [`babel-polyfill`](https://babeljs.io/docs/usage/polyfill/), [`polyfill.io`](https://polyfill.io/) or similar.
#### In top-level projects
Simply include `import 'babel-polyfill';` at the top of your entry file. That statement will be replaced with the necessary polyfills based on the targets you want to support.
```js
// in:
import 'babel-polyfill';
// out:
import 'core-js/modules/es6.object.assign';
import 'core-js/modules/es6.promise';
// ...
```
### Dynamic imports
The support for dynamic imports is enabled by default, please read more on the [`dynamicImport` option](#dynamicImport).
The caveat is that [`preset-env`](https://www.npmjs.com/package/babel-preset-env) is unaware that using `import()` with Webpack relies on Promise internally. Environments which do not have builtin support for Promise, like Internet Explorer, will require both the promise and iterator polyfills be added manually. Having said that, tweak your top-level project's Webpack config like so:
```js
{
entry: [
'core-js/modules/es6.promise',
'core-js/modules/es6.array.iterator',
// Path to your entry file
],
};
```
### Minifying
You must use a minifier that understands ES6+ syntax because the transpiled code might contain ES6+ code.
As an example, UglifyJS v2 only understands ES5 syntax but [UglifyJS v3](https://www.npmjs.com/package/uglify-es) does support ES6+.
## Tests
```sh
$ npm test
$ npm test -- --watch # during development
```
## License
[MIT License](http://opensource.org/licenses/MIT)

View File

@ -0,0 +1,86 @@
'use strict';
const path = require('path');
const addDynamicImportSupport = require('./lib/dynamic-import');
const addReactSupport = require('./lib/react');
const addLodashSupport = require('./lib/lodash');
module.exports = (context, options) => {
options = Object.assign({
loose: true,
react: false,
lodash: true,
dynamicImport: true,
modules: process.env.BABEL_ENV === 'es' ? false : 'commonjs', // Usually set to `commonjs` or `false`
targets: ['browsers', 'node'], // Can be an array with `browsers` and/or `node` or an object
env: process.env.NODE_ENV || 'production',
}, options);
const config = {
sourceType: 'unambiguous',
presets: [],
plugins: [],
};
// The `preset-env` will activate the necessary features based on our targets
// It's no longer necessary to add `es2015`, `es2016`, etc manually
config.presets.push([require.resolve('@babel/preset-env'), {
// This is required to suppress a warning in newer versions of @babel/preset-env
corejs: 3,
// Replaces `import 'babel-polyfill';` with only the polyfills that are
// actually required based on the targets
useBuiltIns: 'entry',
// Produce less and more readable code (although not as faithful to the semantics)
loose: options.loose,
// Set modules options
modules: options.modules,
// Set the browser support to be the same used by Google (https://www.npmjs.com/package/browserslist-config-google)
// Set Nodejs target to be the latest LTS
targets: Array.isArray(options.targets) ? Object.assign({},
options.targets.indexOf('node') !== -1 ? { node: '8.9' } : {},
options.targets.indexOf('browsers') !== -1 ? { browsers: ['extends browserslist-config-google'] } : {}
) : options.targets,
// Enables support for builtin/feature proposals that have native support by the defined target environments
shippedProposals: true,
}]);
// The plugins below activate stage 3 features that babel hasn't added to the stage 3 preset yet
config.plugins.push(
// Allows class { handleClick = () => { } static propTypes = { foo: PropTypes.string } }
[require.resolve('@babel/plugin-proposal-class-properties'), { loose: options.loose }],
// Allows obj?.x?.y?.z
require.resolve('@babel/plugin-proposal-optional-chaining'),
// Allows object.foo ?? 'bar'
require.resolve('@babel/plugin-proposal-nullish-coalescing-operator')
);
config.plugins.push(['@babel/plugin-transform-runtime', {
// We already have preset-env injecting the core-js polyfills automatically
corejs: false,
// Do not replace babel helpers with calls to moduleName
helpers: false,
// Add import calls whenever generatorRuntime global is found
regenerator: true,
// Choose whether to use ESModules or commonjs depending of the env
useESModules: options.modules !== 'commonjs',
// Use the @babel/runtime this package depends on
absoluteRuntime: path.dirname(require.resolve('@babel/runtime/package.json')),
}]);
// Adds dynamic import support
if (options.dynamicImport) {
addDynamicImportSupport(config, options.modules);
}
// Add react support
if (options.react) {
addReactSupport(config, options.env);
}
// Cherry-pick lodash modules for smaller bundles
if (options.lodash) {
addLodashSupport(config, options.lodash);
}
return config;
};

68
babel-preset-moxy/lib.js Normal file
View File

@ -0,0 +1,68 @@
'use strict';
const moduleTransformations = require('@babel/preset-env/lib/module-transformations').default;
const addDynamicImportSupport = require('./lib/dynamic-import');
const addReactSupport = require('./lib/react');
const addLodashSupport = require('./lib/lodash');
module.exports = (context, options) => {
options = Object.assign({
react: false,
lodash: true,
dynamicImport: true,
modules: process.env.BABEL_ENV === 'es' ? false : 'commonjs', // Usually set to `commonjs` or `false`
namedDefaultExport: null,
}, options);
if (options.modules !== 'commonjs' && options.modules !== 'cjs' && options.namedDefaultExport) {
throw new Error('The `namedDefaultExport` option can only be enabled when `modules` is commonjs');
}
// Set `namedDefaultExport` default value based on `modules`
if (options.namedDefaultExport == null) {
options.namedDefaultExport = options.modules === 'commonjs';
}
const config = {
sourceType: 'unambiguous',
presets: [],
plugins: [],
};
// Activate modules transformation
if (moduleTransformations[options.modules]) {
config.plugins.push(`@babel/plugin-${moduleTransformations[options.modules]}`);
}
// The plugins below activate stage 3 features that babel hasn't added to the stage 3 preset yet
config.plugins.push(
// Allows class { handleClick = () => { } static propTypes = { foo: PropTypes.string } }
require.resolve('@babel/plugin-proposal-class-properties'),
// Allows obj?.x?.y?.z
require.resolve('@babel/plugin-proposal-optional-chaining'),
// Allows object.foo ?? 'bar'
require.resolve('@babel/plugin-proposal-nullish-coalescing-operator')
);
// Adds dynamic import support
if (options.dynamicImport) {
addDynamicImportSupport(config, options.modules);
}
// Add react support without doing any development or production transformations
if (options.react) {
addReactSupport(config, null);
}
// Cherry-pick lodash modules for smaller bundles
if (options.lodash) {
addLodashSupport(config, options.lodash);
}
// Add `module.exports = default;`, see https://github.com/59naga/babel-plugin-add-module-exports
if (options.namedDefaultExport) {
config.plugins.push('babel-plugin-add-module-exports');
}
return config;
};

View File

@ -0,0 +1,11 @@
'use strict';
const addDynamicImportSupport = (config, modules) => {
if (modules === false) {
config.plugins.push(require.resolve('@babel/plugin-syntax-dynamic-import'));
} else if (modules === 'cjs' || modules === 'commonjs') {
config.plugins.push(require.resolve('babel-plugin-dynamic-import-node'));
}
};
module.exports = addDynamicImportSupport;

View File

@ -0,0 +1,22 @@
'use strict';
const addLodashSupport = (config, options) => {
// Re-include default ids plus lodash/fp
const baseLodashOptionsIds = [
'lodash',
'lodash-es',
'lodash-compat',
'lodash/fp',
];
config.plugins.push([
require.resolve('babel-plugin-lodash'),
Object.assign(
{},
options,
{ id: baseLodashOptionsIds.concat(options.id || []) }
),
]);
};
module.exports = addLodashSupport;

32
babel-preset-moxy/lib/react.js vendored Normal file
View File

@ -0,0 +1,32 @@
'use strict';
const addReactSupport = (config, env) => {
// Add base support
config.plugins.unshift(
require.resolve('@babel/plugin-syntax-jsx'),
[require.resolve('@babel/plugin-transform-react-jsx'), { useBuiltIns: true }],
require.resolve('@babel/plugin-transform-react-display-name')
);
// Enable optimizations on production
if (env === 'production') {
config.plugins.push(
[require.resolve('babel-plugin-transform-react-remove-prop-types'), { removeImport: true }]
);
// The following two plugins are currently necessary to make React warnings include more valuable information
// They are included here because they are currently not enabled in babel-preset-react
// See the below threads for more info:
// https://github.com/babel/babel/issues/4702
// https://github.com/babel/babel/pull/3540#issuecomment-228673661
// https://github.com/facebookincubator/create-react-app/issues/989
} else if (env === 'development') {
config.plugins.push(
// Adds component stack to warning messages
require.resolve('@babel/plugin-transform-react-jsx-source'),
// Adds __self attribute to JSX which React will use for some warnings
require.resolve('@babel/plugin-transform-react-jsx-self')
);
}
};
module.exports = addReactSupport;

View File

@ -0,0 +1,83 @@
{
"name": "babel-preset-moxy",
"description": "Babel preset to be used at MOXY",
"version": "3.2.0",
"keywords": [
"babel",
"preset",
"react",
"moxy",
"transpile"
],
"author": "André Cruz <andre@moxy.studio>",
"homepage": "https://github.com/moxystudio/babel-preset-moxy",
"repository": {
"type": "git",
"url": "git@github.com:moxystudio/babel-preset-moxy.git"
},
"license": "MIT",
"files": [
"end-project.js",
"lib.js",
"lib"
],
"scripts": {
"lint": "eslint .",
"test": "jest --env node --coverage",
"prerelease": "npm t && npm run lint",
"release": "standard-version"
},
"standard-version": {
"scripts": {
"posttag": "git push --follow-tags origin master"
}
},
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS",
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"*.js": [
"eslint --fix",
"git add"
]
},
"commitlint": {
"extends": [
"@commitlint/config-conventional"
]
},
"dependencies": {
"@babel/plugin-proposal-class-properties": "^7.3.0",
"@babel/plugin-proposal-nullish-coalescing-operator": "^7.4.4",
"@babel/plugin-proposal-optional-chaining": "^7.6.0",
"@babel/plugin-syntax-dynamic-import": "^7.2.0",
"@babel/plugin-syntax-jsx": "^7.2.0",
"@babel/plugin-transform-react-display-name": "^7.2.0",
"@babel/plugin-transform-react-jsx": "^7.3.0",
"@babel/plugin-transform-react-jsx-self": "^7.2.0",
"@babel/plugin-transform-react-jsx-source": "^7.2.0",
"@babel/plugin-transform-runtime": "^7.4.3",
"@babel/preset-env": "^7.4.3",
"@babel/runtime": "^7.4.3",
"babel-plugin-add-module-exports": "^1.0.0",
"babel-plugin-dynamic-import-node": "^2.2.0",
"babel-plugin-lodash": "^3.3.4",
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
"browserslist-config-google": "^1.5.0"
},
"devDependencies": {
"@babel/core": "^7.2.2",
"@commitlint/cli": "^7.1.2",
"@commitlint/config-conventional": "^7.1.2",
"eslint": "^5.5.0",
"eslint-config-moxy": "^7.1.0",
"husky": "^2.1.0",
"jest": "^24.5.0",
"jest-serializer-path": "^0.1.15",
"lint-staged": "^8.1.3",
"standard-version": "^5.0.0"
}
}

55
babel.config.js Normal file
View File

@ -0,0 +1,55 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const semver = require('semver');
const pkg = require('./package.json');
const supportedNodeVersion = semver.minVersion(pkg.engines.node).version;
module.exports = {
babelrcRoots: ['examples/*'],
// we don't wanna run the transforms in this file over react native
exclude: /react-native/,
overrides: [
{
plugins: [
'babel-plugin-replace-ts-export-assignment',
require.resolve(
'./scripts/babel-plugin-jest-replace-ts-require-assignment.js',
),
],
presets: [
[
'@babel/preset-typescript',
{
// will be the default in Babel 8, so let's just turn it on now
allowDeclareFields: true,
// will be default in the future, but we don't want to use it
allowNamespaces: false,
},
],
],
test: /\.tsx?$/,
},
],
plugins: [
['@babel/plugin-transform-modules-commonjs', {allowTopLevelThis: true}],
require.resolve('./scripts/babel-plugin-jest-require-outside-vm'),
],
presets: [
[
'@babel/preset-env',
{
bugfixes: true,
// we manually include the CJS plugin above, so let's make preset-env do less work
modules: false,
shippedProposals: true,
targets: {node: supportedNodeVersion},
},
],
],
};

View File

@ -0,0 +1,9 @@
root = true
[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

2
bcoe-v8-coverage/.gitattributes vendored Normal file
View File

@ -0,0 +1,2 @@
# Enforce `lf` for text files (even on Windows)
text eol=lf

View File

@ -0,0 +1,250 @@
## Next
- **[Breaking change]** Replace `OutModules` enum by custom compiler option `mjsModule`.
- **[Breaking change]** Drop support for Pug, Sass, Angular & Webpack.
- **[Feature]** Expose custom registries for each target.
- **[Feature]** Add `dist.tscOptions` for `lib` target to override options for
distribution builds.
- **[Feature]** Native ESM tests with mocha.
- **[Fix]** Disable deprecated TsLint rules from the default config
- **[Fix]** Remove use of experimental `fs/promises` module.
- **[Internal]** Fix continuous deployment script (stop confusing PRs to master
with push to master)
- **[Internal]** Update dependencies
- **[Internal]** Fix deprecated Mocha types.
## 0.17.1 (2017-05-03)
- **[Fix]** Update dependencies, remove `std/esm` warning.
## 0.17.0 (2017-04-22)
- **[Breaking change]** Update dependencies. Use `esm` instead of `@std/esm`, update Typescript to `2.8.3`.
- **[Fix]** Fix Node processes spawn on Windows (Mocha, Nyc)
## 0.16.2 (2017-02-07)
- **[Fix]** Fix Typedoc generation: use `tsconfig.json` generated for the lib.
- **[Fix]** Write source map for `.mjs` files
- **[Fix]** Copy sources to `_src` when publishing a lib (#87).
- **[Internal]** Restore continuous deployment of documentation.
## 0.16.1 (2017-01-20)
- **[Feature]** Support `mocha` tests on `.mjs` files (using `@std/esm`). Enabled by default
if `outModules` is configured to emit `.mjs`. **You currently need to add
`"@std/esm": {"esm": "cjs"}` to your `package.json`.**
## 0.16.0 (2017-01-09)
- **[Breaking change]** Enable `allowSyntheticDefaultImports` and `esModuleInterop` by default
- **[Fix]** Allow deep module imports in default Tslint rules
- **[Fix]** Drop dependency on deprecated `gulp-util`
- **[Internal]** Replace most custom typings by types from `@types`
## 0.15.8 (2017-12-05)
- **[Fix]** Exit with non-zero code if command tested with coverage fails
- **[Fix]** Solve duplicated error message when using the `run` mocha task.
- **[Fix]** Exit with non-zero code when building scripts fails.
## 0.15.7 (2017-11-29)
- **[Feature]** Add `coverage` task to `mocha` target, use it for the default task
## 0.15.6 (2017-11-29)
- **[Fix]** Fix path to source in source maps.
- **[Fix]** Disable `number-literal-format` in default Tslint rules. It enforced uppercase for hex.
- **[Internal]** Enable integration with Greenkeeper.
- **[Internal]** Enable integration with Codecov
- **[Internal]** Enable code coverage
## 0.15.5 (2017-11-10)
- **[Feature]** Enable the following TsLint rules: `no-duplicate-switch-case`, `no-implicit-dependencies`,
`no-return-await`
- **[Internal]** Update self-dependency `0.15.4`, this restores the README on _npm_
- **[Internal]** Add homepage and author fields to package.json
## 0.15.4 (2017-11-10)
- **[Fix]** Add support for custom additional copy for distribution builds. [#49](https://github.com/demurgos/turbo-gulp/issues/49)
- **[Internal]** Update self-dependency to `turbo-gulp`
- **[Internal]** Add link to license in `README.md`
## 0.15.3 (2017-11-09)
**Rename to `turbo-gulp`**. This package was previously named `demurgos-web-build-tools`.
This version is fully compatible: you can just change the name of your dependency.
## 0.15.2 (2017-11-09)
**The package is prepared to be renamed `turbo-gulp`.**
This is the last version released as `demurgos-web-build-tools`.
- **[Feature]** Add support for watch mode for library targets.
- **[Fix]** Disable experimental support for `*.mjs` by default.
- **[Fix]** Do not emit duplicate TS errors
## 0.15.1 (2017-10-19)
- **[Feature]** Add experimental support for `*.mjs` files
- **[Fix]** Fix support of releases from Continuous Deployment using Travis.
## 0.15.0 (2017-10-18)
- **[Fix]** Add error handling for git deployment.
- **[Internal]** Enable continuous deployment of the `master` branch.
## 0.15.0-beta.11 (2017-08-29)
- **[Feature]** Add `LibTarget.dist.copySrc` option to disable copy of source files to the dist directory.
This allows to prevent issues with missing custom typings.
- **[Fix]** Mark `deploy` property of `LibTarget.typedoc` as optional.
- **[Internal]** Update self-dependency to `v0.15.0-beta.10`.
## 0.15.0-beta.10 (2017-08-28)
- **[Breaking]** Update Tslint rules to use `tslint@5.7.0`.
- **[Fix]** Set `allowJs` to false in default TSC options.
- **[Fix]** Do not pipe output of git commands to stdout.
- **[Internal]** Update self-dependency to `v0.15.0-beta.9`.
## 0.15.0-beta.9 (2017-08-28)
- **[Breaking]** Drop old-style `test` target.
- **[Breaking]** Drop old-style `node` target.
- **[Feature]** Add `mocha` target to run tests in `spec.ts` files.
- **[Feature]** Add `node` target to build and run top-level Node applications.
- **[Feature]** Provide `generateNodeTasks`, `generateLibTasks` and `generateMochaTasks` functions.
They create the tasks but do not register them.
- **[Fix]** Run `clean` before `dist`, if defined.
- **[Fix]** Run `dist` before `publish`.
## 0.15.0-beta.8 (2017-08-26)
- **[Fix]** Remove auth token and registry options for `<lib>:dist:publish`. It is better served
by configuring the environment appropriately.
## 0.15.0-beta.7 (2017-08-26)
- **[Feature]** Add `clean` task to `lib` targets.
- **[Fix]** Ensure that `gitHead` is defined when publishing a package to npm.
## 0.15.0-beta.6 (2017-08-22)
- **[Feature]** Add support for Typedoc deployment to a remote git branch (such as `gh-pages`)
- **[Feature]** Add support for `copy` tasks in new library target.
- **[Fix]** Resolve absolute paths when compiling scripts with custom typings.
## 0.15.0-beta.5 (2017-08-14)
- **[Fix]** Fix package entry for the main module.
## 0.15.0-beta.4 (2017-08-14)
- **[Breaking]** Drop ES5 build exposed to browsers with the `browser` field in `package.json`.
- **[Feature]** Introduce first new-style target (`LibTarget`). it supports typedoc generation, dev builds and
simple distribution.
## 0.15.0-beta.3 (2017-08-11)
- **[Breaking]** Update default lib target to use target-specific `srcDir`.
- **[Feature]** Allow to complete `srcDir` in target.
- **[Feature]** Add experimental library distribution supporting deep requires.
## 0.15.0-beta.2 (2017-08-10)
- **[Fix]** Default to CommonJS for project tsconfig.json
- **[Fix]** Add Typescript configuration for default project.
- **[Internal]** Update self-dependency to `0.15.0-beta.1`.
## 0.15.0-beta.1 (2017-08-09)
- **[Feature]** Support typed TSLint rules.
- **[Internal]** Update gulpfile.ts to use build tools `0.15.0-beta.0`.
- **[Fix]** Fix regressions caused by `0.15.0-beta.0` (missing type definition).
## 0.15.0-beta.0 (2017-08-09)
- **[Breaking]** Expose option interfaces directly in the main module instead of the `config` namespace.
- **[Breaking]** Rename `DEFAULT_PROJECT_OPTIONS` to `DEFAULT_PROJECT`.
- **[Feature]** Emit project-wide `tsconfig.json`.
- **[Internal]** Convert gulpfile to Typescript, use `ts-node` to run it.
- **[Internal]** Update dependencies
## 0.14.3 (2017-07-16)
- **[Feature]** Add `:lint:fix` project task to fix some lint errors.
## 0.14.2 (2017-07-10)
- **[Internal]** Update dependencies: add `package-lock.json` and update `tslint`.
## 0.14.1 (2017-06-17)
- **[Internal]** Update dependencies.
- **[Internal]** Drop dependency on _Bluebird_.
- **[Internal]** Drop dependency on _typings_.
## 0.14.0 (2017-05-10)
- **[Breaking]** Enforce trailing commas by default for multiline objects
- **[Feature]** Allow bump from either `master` or a branch with the same name as the tag (exampel: `v1.2.3`)
- **[Feature]** Support TSLint 8, allow to extend the default rules
- **[Patch]** Allow mergeable namespaces
# 0.13.1
- **[Patch]** Allow namespaces in the default TS-Lint config
# 0.13.0
- **[Breaking]** Major overhaul of the angular target. The server build no longer depends on the client.
- **[Breaking]** Update to `gulp@4` (from `gulp@3`)
- **[Breaking]** Update to `tslint@7` (from `tslint@6`), add stricter default rules
- **[Breaking]** Update signature of targetGenerators and project tasks: it only uses
`ProjectOptions` and `Target` now, the additional options are embedded in those two objects.
- **[Breaking]** Remove `:install`, `:instal:npm` and `:install:typings`. Use the `prepare` script in
your `package.json` file instead.
- Add `:tslint.json` project task to generate configuration for `tslint`
- Add first class support for processing of `pug` and `sass` files, similar to `copy`
- Implement end-to-end tests
- Enable `emitDecoratorMetadata` in default typescript options.
- Allow configuration of `:lint` with the `tslintOptions` property of the project configuration.
- Add `<target>:watch` tasks for incremental builds.
# 0.12.3
- Support `templateUrl` and `styleUrls` in angular modules.
# 0.12.2
- Add `<target>:build:copy` task. It copies user-defined files.
# 0.12.1
- Fix `<target>:watch` task.
# 0.12.0
- **[Breaking]**: Change naming convention for tasks. The names primary part is
the target, then the action (`lib:build` instead of `build:lib`) to group
the tasks per target.
- **[Breaking]**: Use `typeRoots` instead of `definitions` in configuration to
specify Typescript definition files.
- Generate `tsconfig.json` file (mainly for editors)
- Implement the `test` target to run unit-tests with `mocha`.
# 0.11.2
- Target `angular`: Add `build:<target>:assets:sass` for `.scss` files (Sassy CSS)
# 0.11.1
- Rename project to `web-build-tools` (`demurgos-web-build-tools` on _npm_)
- Target `angular`: Add `build:<target>:assets`, `build:<target>:pug` and `build:<target>:static`.
- Update `gulp-typescript`: solve error message during compilation
- Targets `node` and `angular`: `build:<target>:scripts` now include in-lined source maps
- Target `node`: `watch:<target>` to support incremental builds

View File

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright © 2015-2017 Charles Samborski
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,14 @@
Copyright (c) 2017, Contributors
Permission to use, copy, modify, and/or distribute this software
for any purpose with or without fee is hereby granted, provided
that the above copyright notice and this permission notice
appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE
LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES
OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.

View File

@ -0,0 +1,11 @@
# V8 Coverage
[![npm](https://img.shields.io/npm/v/@c88/v8-coverage.svg?maxAge=2592000)](https://www.npmjs.com/package/@c88/v8-coverage)
[![GitHub repository](https://img.shields.io/badge/Github-demurgos%2Fv8--coverage-blue.svg)](https://github.com/demurgos/v8-coverage)
[![Build status (Travis)](https://img.shields.io/travis/demurgos/v8-coverage/master.svg?maxAge=2592000)](https://travis-ci.org/demurgos/v8-coverage)
[![Build status (AppVeyor)](https://ci.appveyor.com/api/projects/status/qgcbdffyb9e09d0e?svg=true)](https://ci.appveyor.com/project/demurgos/v8-coverage)
[![Codecov](https://codecov.io/gh/demurgos/v8-coverage/branch/master/graph/badge.svg)](https://codecov.io/gh/demurgos/v8-coverage)
## License
[MIT License](./LICENSE.md)

View File

@ -0,0 +1,95 @@
import * as buildTools from "turbo-gulp";
import { LibTarget, registerLibTasks } from "turbo-gulp/targets/lib";
import { MochaTarget, registerMochaTasks } from "turbo-gulp/targets/mocha";
import gulp from "gulp";
import minimist from "minimist";
interface Options {
devDist?: string;
}
const options: Options & minimist.ParsedArgs = minimist(process.argv.slice(2), {
string: ["devDist"],
default: {devDist: undefined},
alias: {devDist: "dev-dist"},
});
const project: buildTools.Project = {
root: __dirname,
packageJson: "package.json",
buildDir: "build",
distDir: "dist",
srcDir: "src",
typescript: {}
};
const lib: LibTarget = {
project,
name: "lib",
srcDir: "src/lib",
scripts: ["**/*.ts"],
mainModule: "index",
dist: {
packageJsonMap: (old: buildTools.PackageJson): buildTools.PackageJson => {
const version: string = options.devDist !== undefined ? `${old.version}-build.${options.devDist}` : old.version;
return <any> {...old, version, scripts: undefined, private: false};
},
npmPublish: {
tag: options.devDist !== undefined ? "next" : "latest",
},
},
tscOptions: {
declaration: true,
skipLibCheck: true,
},
typedoc: {
dir: "typedoc",
name: "Helpers for V8 coverage files",
deploy: {
repository: "git@github.com:demurgos/v8-coverage.git",
branch: "gh-pages",
},
},
copy: [
{
files: ["**/*.json"],
},
],
clean: {
dirs: ["build/lib", "dist/lib"],
},
};
const test: MochaTarget = {
project,
name: "test",
srcDir: "src",
scripts: ["test/**/*.ts", "lib/**/*.ts", "e2e/*/*.ts"],
customTypingsDir: "src/custom-typings",
tscOptions: {
allowSyntheticDefaultImports: true,
esModuleInterop: true,
skipLibCheck: true,
},
// generateTestMain: true,
copy: [
{
src: "e2e",
// <project-name>/(project|test-resources)/<any>
files: ["*/project/**/*", "*/test-resources/**/*"],
dest: "e2e",
},
],
clean: {
dirs: ["build/test"],
},
};
const libTasks: any = registerLibTasks(gulp, lib);
registerMochaTasks(gulp, test);
buildTools.projectTasks.registerAll(gulp, project);
gulp.task("all:tsconfig.json", gulp.parallel("lib:tsconfig.json", "test:tsconfig.json"));
gulp.task("dist", libTasks.dist);
gulp.task("default", libTasks.dist);

View File

@ -0,0 +1,48 @@
{
"name": "@bcoe/v8-coverage",
"version": "0.2.3",
"description": "Helper functions for V8 coverage files.",
"author": "Charles Samborski <demurgos@demurgos.net> (https://demurgos.net)",
"license": "MIT",
"main": "dist/lib/index",
"types": "dist/lib/index.d.ts",
"repository": {
"type": "git",
"url": "git://github.com/demurgos/v8-coverage.git"
},
"homepage": "https://demurgos.github.io/v8-coverage",
"scripts": {
"prepare": "gulp all:tsconfig.json && gulp dist",
"pretest": "gulp lib:build",
"test": "gulp test",
"lint": "gulp :lint:fix"
},
"devDependencies": {
"@types/chai": "^4.1.4",
"@types/gulp": "^4.0.5",
"@types/minimist": "^1.2.0",
"@types/mocha": "^5.2.2",
"@types/node": "^10.5.4",
"chai": "^4.1.2",
"codecov": "^3.0.2",
"gulp": "^4.0.0",
"gulp-cli": "^2.0.1",
"minimist": "^1.2.0",
"pre-commit": "^1.2.2",
"ts-node": "^8.3.0",
"turbo-gulp": "^0.20.1"
},
"nyc": {
"include": [
"build/test/lib/**/*.js",
"build/test/lib/**/*.mjs"
],
"reporter": [
"text",
"html"
],
"extension": [
".mjs"
]
}
}

View File

@ -0,0 +1,146 @@
import { compareRangeCovs } from "./compare";
import { RangeCov } from "./types";
interface ReadonlyRangeTree {
readonly start: number;
readonly end: number;
readonly count: number;
readonly children: ReadonlyRangeTree[];
}
export function emitForest(trees: ReadonlyArray<ReadonlyRangeTree>): string {
return emitForestLines(trees).join("\n");
}
export function emitForestLines(trees: ReadonlyArray<ReadonlyRangeTree>): string[] {
const colMap: Map<number, number> = getColMap(trees);
const header: string = emitOffsets(colMap);
return [header, ...trees.map(tree => emitTree(tree, colMap).join("\n"))];
}
function getColMap(trees: Iterable<ReadonlyRangeTree>): Map<number, number> {
const eventSet: Set<number> = new Set();
for (const tree of trees) {
const stack: ReadonlyRangeTree[] = [tree];
while (stack.length > 0) {
const cur: ReadonlyRangeTree = stack.pop()!;
eventSet.add(cur.start);
eventSet.add(cur.end);
for (const child of cur.children) {
stack.push(child);
}
}
}
const events: number[] = [...eventSet];
events.sort((a, b) => a - b);
let maxDigits: number = 1;
for (const event of events) {
maxDigits = Math.max(maxDigits, event.toString(10).length);
}
const colWidth: number = maxDigits + 3;
const colMap: Map<number, number> = new Map();
for (const [i, event] of events.entries()) {
colMap.set(event, i * colWidth);
}
return colMap;
}
function emitTree(tree: ReadonlyRangeTree, colMap: Map<number, number>): string[] {
const layers: ReadonlyRangeTree[][] = [];
let nextLayer: ReadonlyRangeTree[] = [tree];
while (nextLayer.length > 0) {
const layer: ReadonlyRangeTree[] = nextLayer;
layers.push(layer);
nextLayer = [];
for (const node of layer) {
for (const child of node.children) {
nextLayer.push(child);
}
}
}
return layers.map(layer => emitTreeLayer(layer, colMap));
}
export function parseFunctionRanges(text: string, offsetMap: Map<number, number>): RangeCov[] {
const result: RangeCov[] = [];
for (const line of text.split("\n")) {
for (const range of parseTreeLayer(line, offsetMap)) {
result.push(range);
}
}
result.sort(compareRangeCovs);
return result;
}
/**
*
* @param layer Sorted list of disjoint trees.
* @param colMap
*/
function emitTreeLayer(layer: ReadonlyRangeTree[], colMap: Map<number, number>): string {
const line: string[] = [];
let curIdx: number = 0;
for (const {start, end, count} of layer) {
const startIdx: number = colMap.get(start)!;
const endIdx: number = colMap.get(end)!;
if (startIdx > curIdx) {
line.push(" ".repeat(startIdx - curIdx));
}
line.push(emitRange(count, endIdx - startIdx));
curIdx = endIdx;
}
return line.join("");
}
function parseTreeLayer(text: string, offsetMap: Map<number, number>): RangeCov[] {
const result: RangeCov[] = [];
const regex: RegExp = /\[(\d+)-*\)/gs;
while (true) {
const match: RegExpMatchArray | null = regex.exec(text);
if (match === null) {
break;
}
const startIdx: number = match.index!;
const endIdx: number = startIdx + match[0].length;
const count: number = parseInt(match[1], 10);
const startOffset: number | undefined = offsetMap.get(startIdx);
const endOffset: number | undefined = offsetMap.get(endIdx);
if (startOffset === undefined || endOffset === undefined) {
throw new Error(`Invalid offsets for: ${JSON.stringify(text)}`);
}
result.push({startOffset, endOffset, count});
}
return result;
}
function emitRange(count: number, len: number): string {
const rangeStart: string = `[${count.toString(10)}`;
const rangeEnd: string = ")";
const hyphensLen: number = len - (rangeStart.length + rangeEnd.length);
const hyphens: string = "-".repeat(Math.max(0, hyphensLen));
return `${rangeStart}${hyphens}${rangeEnd}`;
}
function emitOffsets(colMap: Map<number, number>): string {
let line: string = "";
for (const [event, col] of colMap) {
if (line.length < col) {
line += " ".repeat(col - line.length);
}
line += event.toString(10);
}
return line;
}
export function parseOffsets(text: string): Map<number, number> {
const result: Map<number, number> = new Map();
const regex: RegExp = /\d+/gs;
while (true) {
const match: RegExpExecArray | null = regex.exec(text);
if (match === null) {
break;
}
result.set(match.index, parseInt(match[0], 10));
}
return result;
}

View File

@ -0,0 +1,70 @@
import { FunctionCov, ProcessCov, RangeCov, ScriptCov } from "./types";
/**
* Creates a deep copy of a process coverage.
*
* @param processCov Process coverage to clone.
* @return Cloned process coverage.
*/
export function cloneProcessCov(processCov: Readonly<ProcessCov>): ProcessCov {
const result: ScriptCov[] = [];
for (const scriptCov of processCov.result) {
result.push(cloneScriptCov(scriptCov));
}
return {
result,
};
}
/**
* Creates a deep copy of a script coverage.
*
* @param scriptCov Script coverage to clone.
* @return Cloned script coverage.
*/
export function cloneScriptCov(scriptCov: Readonly<ScriptCov>): ScriptCov {
const functions: FunctionCov[] = [];
for (const functionCov of scriptCov.functions) {
functions.push(cloneFunctionCov(functionCov));
}
return {
scriptId: scriptCov.scriptId,
url: scriptCov.url,
functions,
};
}
/**
* Creates a deep copy of a function coverage.
*
* @param functionCov Function coverage to clone.
* @return Cloned function coverage.
*/
export function cloneFunctionCov(functionCov: Readonly<FunctionCov>): FunctionCov {
const ranges: RangeCov[] = [];
for (const rangeCov of functionCov.ranges) {
ranges.push(cloneRangeCov(rangeCov));
}
return {
functionName: functionCov.functionName,
ranges,
isBlockCoverage: functionCov.isBlockCoverage,
};
}
/**
* Creates a deep copy of a function coverage.
*
* @param rangeCov Range coverage to clone.
* @return Cloned range coverage.
*/
export function cloneRangeCov(rangeCov: Readonly<RangeCov>): RangeCov {
return {
startOffset: rangeCov.startOffset,
endOffset: rangeCov.endOffset,
count: rangeCov.count,
};
}

View File

@ -0,0 +1,40 @@
import { FunctionCov, RangeCov, ScriptCov } from "./types";
/**
* Compares two script coverages.
*
* The result corresponds to the comparison of their `url` value (alphabetical sort).
*/
export function compareScriptCovs(a: Readonly<ScriptCov>, b: Readonly<ScriptCov>): number {
if (a.url === b.url) {
return 0;
} else if (a.url < b.url) {
return -1;
} else {
return 1;
}
}
/**
* Compares two function coverages.
*
* The result corresponds to the comparison of the root ranges.
*/
export function compareFunctionCovs(a: Readonly<FunctionCov>, b: Readonly<FunctionCov>): number {
return compareRangeCovs(a.ranges[0], b.ranges[0]);
}
/**
* Compares two range coverages.
*
* The ranges are first ordered by ascending `startOffset` and then by
* descending `endOffset`.
* This corresponds to a pre-order tree traversal.
*/
export function compareRangeCovs(a: Readonly<RangeCov>, b: Readonly<RangeCov>): number {
if (a.startOffset !== b.startOffset) {
return a.startOffset - b.startOffset;
} else {
return b.endOffset - a.endOffset;
}
}

View File

@ -0,0 +1,6 @@
export { emitForest, emitForestLines, parseFunctionRanges, parseOffsets } from "./ascii";
export { cloneFunctionCov, cloneProcessCov, cloneScriptCov, cloneRangeCov } from "./clone";
export { compareScriptCovs, compareFunctionCovs, compareRangeCovs } from "./compare";
export { mergeFunctionCovs, mergeProcessCovs, mergeScriptCovs } from "./merge";
export { RangeTree } from "./range-tree";
export { ProcessCov, ScriptCov, FunctionCov, RangeCov } from "./types";

View File

@ -0,0 +1,343 @@
import {
deepNormalizeScriptCov,
normalizeFunctionCov,
normalizeProcessCov,
normalizeRangeTree,
normalizeScriptCov,
} from "./normalize";
import { RangeTree } from "./range-tree";
import { FunctionCov, ProcessCov, Range, RangeCov, ScriptCov } from "./types";
/**
* Merges a list of process coverages.
*
* The result is normalized.
* The input values may be mutated, it is not safe to use them after passing
* them to this function.
* The computation is synchronous.
*
* @param processCovs Process coverages to merge.
* @return Merged process coverage.
*/
export function mergeProcessCovs(processCovs: ReadonlyArray<ProcessCov>): ProcessCov {
if (processCovs.length === 0) {
return {result: []};
}
const urlToScripts: Map<string, ScriptCov[]> = new Map();
for (const processCov of processCovs) {
for (const scriptCov of processCov.result) {
let scriptCovs: ScriptCov[] | undefined = urlToScripts.get(scriptCov.url);
if (scriptCovs === undefined) {
scriptCovs = [];
urlToScripts.set(scriptCov.url, scriptCovs);
}
scriptCovs.push(scriptCov);
}
}
const result: ScriptCov[] = [];
for (const scripts of urlToScripts.values()) {
// assert: `scripts.length > 0`
result.push(mergeScriptCovs(scripts)!);
}
const merged: ProcessCov = {result};
normalizeProcessCov(merged);
return merged;
}
/**
* Merges a list of matching script coverages.
*
* Scripts are matching if they have the same `url`.
* The result is normalized.
* The input values may be mutated, it is not safe to use them after passing
* them to this function.
* The computation is synchronous.
*
* @param scriptCovs Process coverages to merge.
* @return Merged script coverage, or `undefined` if the input list was empty.
*/
export function mergeScriptCovs(scriptCovs: ReadonlyArray<ScriptCov>): ScriptCov | undefined {
if (scriptCovs.length === 0) {
return undefined;
} else if (scriptCovs.length === 1) {
const merged: ScriptCov = scriptCovs[0];
deepNormalizeScriptCov(merged);
return merged;
}
const first: ScriptCov = scriptCovs[0];
const scriptId: string = first.scriptId;
const url: string = first.url;
const rangeToFuncs: Map<string, FunctionCov[]> = new Map();
for (const scriptCov of scriptCovs) {
for (const funcCov of scriptCov.functions) {
const rootRange: string = stringifyFunctionRootRange(funcCov);
let funcCovs: FunctionCov[] | undefined = rangeToFuncs.get(rootRange);
if (funcCovs === undefined ||
// if the entry in rangeToFuncs is function-level granularity and
// the new coverage is block-level, prefer block-level.
(!funcCovs[0].isBlockCoverage && funcCov.isBlockCoverage)) {
funcCovs = [];
rangeToFuncs.set(rootRange, funcCovs);
} else if (funcCovs[0].isBlockCoverage && !funcCov.isBlockCoverage) {
// if the entry in rangeToFuncs is block-level granularity, we should
// not append function level granularity.
continue;
}
funcCovs.push(funcCov);
}
}
const functions: FunctionCov[] = [];
for (const funcCovs of rangeToFuncs.values()) {
// assert: `funcCovs.length > 0`
functions.push(mergeFunctionCovs(funcCovs)!);
}
const merged: ScriptCov = {scriptId, url, functions};
normalizeScriptCov(merged);
return merged;
}
/**
* Returns a string representation of the root range of the function.
*
* This string can be used to match function with same root range.
* The string is derived from the start and end offsets of the root range of
* the function.
* This assumes that `ranges` is non-empty (true for valid function coverages).
*
* @param funcCov Function coverage with the range to stringify
* @internal
*/
function stringifyFunctionRootRange(funcCov: Readonly<FunctionCov>): string {
const rootRange: RangeCov = funcCov.ranges[0];
return `${rootRange.startOffset.toString(10)};${rootRange.endOffset.toString(10)}`;
}
/**
* Merges a list of matching function coverages.
*
* Functions are matching if their root ranges have the same span.
* The result is normalized.
* The input values may be mutated, it is not safe to use them after passing
* them to this function.
* The computation is synchronous.
*
* @param funcCovs Function coverages to merge.
* @return Merged function coverage, or `undefined` if the input list was empty.
*/
export function mergeFunctionCovs(funcCovs: ReadonlyArray<FunctionCov>): FunctionCov | undefined {
if (funcCovs.length === 0) {
return undefined;
} else if (funcCovs.length === 1) {
const merged: FunctionCov = funcCovs[0];
normalizeFunctionCov(merged);
return merged;
}
const functionName: string = funcCovs[0].functionName;
const trees: RangeTree[] = [];
for (const funcCov of funcCovs) {
// assert: `fn.ranges.length > 0`
// assert: `fn.ranges` is sorted
trees.push(RangeTree.fromSortedRanges(funcCov.ranges)!);
}
// assert: `trees.length > 0`
const mergedTree: RangeTree = mergeRangeTrees(trees)!;
normalizeRangeTree(mergedTree);
const ranges: RangeCov[] = mergedTree.toRanges();
const isBlockCoverage: boolean = !(ranges.length === 1 && ranges[0].count === 0);
const merged: FunctionCov = {functionName, ranges, isBlockCoverage};
// assert: `merged` is normalized
return merged;
}
/**
* @precondition Same `start` and `end` for all the trees
*/
function mergeRangeTrees(trees: ReadonlyArray<RangeTree>): RangeTree | undefined {
if (trees.length <= 1) {
return trees[0];
}
const first: RangeTree = trees[0];
let delta: number = 0;
for (const tree of trees) {
delta += tree.delta;
}
const children: RangeTree[] = mergeRangeTreeChildren(trees);
return new RangeTree(first.start, first.end, delta, children);
}
class RangeTreeWithParent {
readonly parentIndex: number;
readonly tree: RangeTree;
constructor(parentIndex: number, tree: RangeTree) {
this.parentIndex = parentIndex;
this.tree = tree;
}
}
class StartEvent {
readonly offset: number;
readonly trees: RangeTreeWithParent[];
constructor(offset: number, trees: RangeTreeWithParent[]) {
this.offset = offset;
this.trees = trees;
}
static compare(a: StartEvent, b: StartEvent): number {
return a.offset - b.offset;
}
}
class StartEventQueue {
private readonly queue: StartEvent[];
private nextIndex: number;
private pendingOffset: number;
private pendingTrees: RangeTreeWithParent[] | undefined;
private constructor(queue: StartEvent[]) {
this.queue = queue;
this.nextIndex = 0;
this.pendingOffset = 0;
this.pendingTrees = undefined;
}
static fromParentTrees(parentTrees: ReadonlyArray<RangeTree>): StartEventQueue {
const startToTrees: Map<number, RangeTreeWithParent[]> = new Map();
for (const [parentIndex, parentTree] of parentTrees.entries()) {
for (const child of parentTree.children) {
let trees: RangeTreeWithParent[] | undefined = startToTrees.get(child.start);
if (trees === undefined) {
trees = [];
startToTrees.set(child.start, trees);
}
trees.push(new RangeTreeWithParent(parentIndex, child));
}
}
const queue: StartEvent[] = [];
for (const [startOffset, trees] of startToTrees) {
queue.push(new StartEvent(startOffset, trees));
}
queue.sort(StartEvent.compare);
return new StartEventQueue(queue);
}
setPendingOffset(offset: number): void {
this.pendingOffset = offset;
}
pushPendingTree(tree: RangeTreeWithParent): void {
if (this.pendingTrees === undefined) {
this.pendingTrees = [];
}
this.pendingTrees.push(tree);
}
next(): StartEvent | undefined {
const pendingTrees: RangeTreeWithParent[] | undefined = this.pendingTrees;
const nextEvent: StartEvent | undefined = this.queue[this.nextIndex];
if (pendingTrees === undefined) {
this.nextIndex++;
return nextEvent;
} else if (nextEvent === undefined) {
this.pendingTrees = undefined;
return new StartEvent(this.pendingOffset, pendingTrees);
} else {
if (this.pendingOffset < nextEvent.offset) {
this.pendingTrees = undefined;
return new StartEvent(this.pendingOffset, pendingTrees);
} else {
if (this.pendingOffset === nextEvent.offset) {
this.pendingTrees = undefined;
for (const tree of pendingTrees) {
nextEvent.trees.push(tree);
}
}
this.nextIndex++;
return nextEvent;
}
}
}
}
function mergeRangeTreeChildren(parentTrees: ReadonlyArray<RangeTree>): RangeTree[] {
const result: RangeTree[] = [];
const startEventQueue: StartEventQueue = StartEventQueue.fromParentTrees(parentTrees);
const parentToNested: Map<number, RangeTree[]> = new Map();
let openRange: Range | undefined;
while (true) {
const event: StartEvent | undefined = startEventQueue.next();
if (event === undefined) {
break;
}
if (openRange !== undefined && openRange.end <= event.offset) {
result.push(nextChild(openRange, parentToNested));
openRange = undefined;
}
if (openRange === undefined) {
let openRangeEnd: number = event.offset + 1;
for (const {parentIndex, tree} of event.trees) {
openRangeEnd = Math.max(openRangeEnd, tree.end);
insertChild(parentToNested, parentIndex, tree);
}
startEventQueue.setPendingOffset(openRangeEnd);
openRange = {start: event.offset, end: openRangeEnd};
} else {
for (const {parentIndex, tree} of event.trees) {
if (tree.end > openRange.end) {
const right: RangeTree = tree.split(openRange.end);
startEventQueue.pushPendingTree(new RangeTreeWithParent(parentIndex, right));
}
insertChild(parentToNested, parentIndex, tree);
}
}
}
if (openRange !== undefined) {
result.push(nextChild(openRange, parentToNested));
}
return result;
}
function insertChild(parentToNested: Map<number, RangeTree[]>, parentIndex: number, tree: RangeTree): void {
let nested: RangeTree[] | undefined = parentToNested.get(parentIndex);
if (nested === undefined) {
nested = [];
parentToNested.set(parentIndex, nested);
}
nested.push(tree);
}
function nextChild(openRange: Range, parentToNested: Map<number, RangeTree[]>): RangeTree {
const matchingTrees: RangeTree[] = [];
for (const nested of parentToNested.values()) {
if (nested.length === 1 && nested[0].start === openRange.start && nested[0].end === openRange.end) {
matchingTrees.push(nested[0]);
} else {
matchingTrees.push(new RangeTree(
openRange.start,
openRange.end,
0,
nested,
));
}
}
parentToNested.clear();
return mergeRangeTrees(matchingTrees)!;
}

View File

@ -0,0 +1,84 @@
import { compareFunctionCovs, compareRangeCovs, compareScriptCovs } from "./compare";
import { RangeTree } from "./range-tree";
import { FunctionCov, ProcessCov, ScriptCov } from "./types";
/**
* Normalizes a process coverage.
*
* Sorts the scripts alphabetically by `url`.
* Reassigns script ids: the script at index `0` receives `"0"`, the script at
* index `1` receives `"1"` etc.
* This does not normalize the script coverages.
*
* @param processCov Process coverage to normalize.
*/
export function normalizeProcessCov(processCov: ProcessCov): void {
processCov.result.sort(compareScriptCovs);
for (const [scriptId, scriptCov] of processCov.result.entries()) {
scriptCov.scriptId = scriptId.toString(10);
}
}
/**
* Normalizes a process coverage deeply.
*
* Normalizes the script coverages deeply, then normalizes the process coverage
* itself.
*
* @param processCov Process coverage to normalize.
*/
export function deepNormalizeProcessCov(processCov: ProcessCov): void {
for (const scriptCov of processCov.result) {
deepNormalizeScriptCov(scriptCov);
}
normalizeProcessCov(processCov);
}
/**
* Normalizes a script coverage.
*
* Sorts the function by root range (pre-order sort).
* This does not normalize the function coverages.
*
* @param scriptCov Script coverage to normalize.
*/
export function normalizeScriptCov(scriptCov: ScriptCov): void {
scriptCov.functions.sort(compareFunctionCovs);
}
/**
* Normalizes a script coverage deeply.
*
* Normalizes the function coverages deeply, then normalizes the script coverage
* itself.
*
* @param scriptCov Script coverage to normalize.
*/
export function deepNormalizeScriptCov(scriptCov: ScriptCov): void {
for (const funcCov of scriptCov.functions) {
normalizeFunctionCov(funcCov);
}
normalizeScriptCov(scriptCov);
}
/**
* Normalizes a function coverage.
*
* Sorts the ranges (pre-order sort).
* TODO: Tree-based normalization of the ranges.
*
* @param funcCov Function coverage to normalize.
*/
export function normalizeFunctionCov(funcCov: FunctionCov): void {
funcCov.ranges.sort(compareRangeCovs);
const tree: RangeTree = RangeTree.fromSortedRanges(funcCov.ranges)!;
normalizeRangeTree(tree);
funcCov.ranges = tree.toRanges();
}
/**
* @internal
*/
export function normalizeRangeTree(tree: RangeTree): void {
tree.normalize();
}

View File

@ -0,0 +1,156 @@
import { RangeCov } from "./types";
export class RangeTree {
start: number;
end: number;
delta: number;
children: RangeTree[];
constructor(
start: number,
end: number,
delta: number,
children: RangeTree[],
) {
this.start = start;
this.end = end;
this.delta = delta;
this.children = children;
}
/**
* @precodition `ranges` are well-formed and pre-order sorted
*/
static fromSortedRanges(ranges: ReadonlyArray<RangeCov>): RangeTree | undefined {
let root: RangeTree | undefined;
// Stack of parent trees and parent counts.
const stack: [RangeTree, number][] = [];
for (const range of ranges) {
const node: RangeTree = new RangeTree(range.startOffset, range.endOffset, range.count, []);
if (root === undefined) {
root = node;
stack.push([node, range.count]);
continue;
}
let parent: RangeTree;
let parentCount: number;
while (true) {
[parent, parentCount] = stack[stack.length - 1];
// assert: `top !== undefined` (the ranges are sorted)
if (range.startOffset < parent.end) {
break;
} else {
stack.pop();
}
}
node.delta -= parentCount;
parent.children.push(node);
stack.push([node, range.count]);
}
return root;
}
normalize(): void {
const children: RangeTree[] = [];
let curEnd: number;
let head: RangeTree | undefined;
const tail: RangeTree[] = [];
for (const child of this.children) {
if (head === undefined) {
head = child;
} else if (child.delta === head.delta && child.start === curEnd!) {
tail.push(child);
} else {
endChain();
head = child;
}
curEnd = child.end;
}
if (head !== undefined) {
endChain();
}
if (children.length === 1) {
const child: RangeTree = children[0];
if (child.start === this.start && child.end === this.end) {
this.delta += child.delta;
this.children = child.children;
// `.lazyCount` is zero for both (both are after normalization)
return;
}
}
this.children = children;
function endChain(): void {
if (tail.length !== 0) {
head!.end = tail[tail.length - 1].end;
for (const tailTree of tail) {
for (const subChild of tailTree.children) {
subChild.delta += tailTree.delta - head!.delta;
head!.children.push(subChild);
}
}
tail.length = 0;
}
head!.normalize();
children.push(head!);
}
}
/**
* @precondition `tree.start < value && value < tree.end`
* @return RangeTree Right part
*/
split(value: number): RangeTree {
let leftChildLen: number = this.children.length;
let mid: RangeTree | undefined;
// TODO(perf): Binary search (check overhead)
for (let i: number = 0; i < this.children.length; i++) {
const child: RangeTree = this.children[i];
if (child.start < value && value < child.end) {
mid = child.split(value);
leftChildLen = i + 1;
break;
} else if (child.start >= value) {
leftChildLen = i;
break;
}
}
const rightLen: number = this.children.length - leftChildLen;
const rightChildren: RangeTree[] = this.children.splice(leftChildLen, rightLen);
if (mid !== undefined) {
rightChildren.unshift(mid);
}
const result: RangeTree = new RangeTree(
value,
this.end,
this.delta,
rightChildren,
);
this.end = value;
return result;
}
/**
* Get the range coverages corresponding to the tree.
*
* The ranges are pre-order sorted.
*/
toRanges(): RangeCov[] {
const ranges: RangeCov[] = [];
// Stack of parent trees and counts.
const stack: [RangeTree, number][] = [[this, 0]];
while (stack.length > 0) {
const [cur, parentCount]: [RangeTree, number] = stack.pop()!;
const count: number = parentCount + cur.delta;
ranges.push({startOffset: cur.start, endOffset: cur.end, count});
for (let i: number = cur.children.length - 1; i >= 0; i--) {
stack.push([cur.children[i], count]);
}
}
return ranges;
}
}

View File

@ -0,0 +1,26 @@
export interface ProcessCov {
result: ScriptCov[];
}
export interface ScriptCov {
scriptId: string;
url: string;
functions: FunctionCov[];
}
export interface FunctionCov {
functionName: string;
ranges: RangeCov[];
isBlockCoverage: boolean;
}
export interface Range {
readonly start: number;
readonly end: number;
}
export interface RangeCov {
startOffset: number;
endOffset: number;
count: number;
}

View File

@ -0,0 +1,280 @@
import chai from "chai";
import fs from "fs";
import path from "path";
import { FunctionCov, mergeFunctionCovs, mergeProcessCovs, mergeScriptCovs, ProcessCov, ScriptCov } from "../lib";
const REPO_ROOT: string = path.join(__dirname, "..", "..", "..", "..");
const BENCHES_INPUT_DIR: string = path.join(REPO_ROOT, "benches");
const BENCHES_DIR: string = path.join(REPO_ROOT, "test-data", "merge", "benches");
const RANGES_DIR: string = path.join(REPO_ROOT, "test-data", "merge", "ranges");
const BENCHES_TIMEOUT: number = 20000; // 20sec
interface MergeRangeItem {
name: string;
status: "run" | "skip" | "only";
inputs: ProcessCov[];
expected: ProcessCov;
}
const FIXTURES_DIR: string = path.join(REPO_ROOT, "test-data", "bugs");
function loadFixture(name: string) {
const content: string = fs.readFileSync(
path.resolve(FIXTURES_DIR, `${name}.json`),
{encoding: "UTF-8"},
);
return JSON.parse(content);
}
describe("merge", () => {
describe("Various", () => {
it("accepts empty arrays for `mergeProcessCovs`", () => {
const inputs: ProcessCov[] = [];
const expected: ProcessCov = {result: []};
const actual: ProcessCov = mergeProcessCovs(inputs);
chai.assert.deepEqual(actual, expected);
});
it("accepts empty arrays for `mergeScriptCovs`", () => {
const inputs: ScriptCov[] = [];
const expected: ScriptCov | undefined = undefined;
const actual: ScriptCov | undefined = mergeScriptCovs(inputs);
chai.assert.deepEqual(actual, expected);
});
it("accepts empty arrays for `mergeFunctionCovs`", () => {
const inputs: FunctionCov[] = [];
const expected: FunctionCov | undefined = undefined;
const actual: FunctionCov | undefined = mergeFunctionCovs(inputs);
chai.assert.deepEqual(actual, expected);
});
it("accepts arrays with a single item for `mergeProcessCovs`", () => {
const inputs: ProcessCov[] = [
{
result: [
{
scriptId: "123",
url: "/lib.js",
functions: [
{
functionName: "test",
isBlockCoverage: true,
ranges: [
{startOffset: 0, endOffset: 4, count: 2},
{startOffset: 1, endOffset: 2, count: 1},
{startOffset: 2, endOffset: 3, count: 1},
],
},
],
},
],
},
];
const expected: ProcessCov = {
result: [
{
scriptId: "0",
url: "/lib.js",
functions: [
{
functionName: "test",
isBlockCoverage: true,
ranges: [
{startOffset: 0, endOffset: 4, count: 2},
{startOffset: 1, endOffset: 3, count: 1},
],
},
],
},
],
};
const actual: ProcessCov = mergeProcessCovs(inputs);
chai.assert.deepEqual(actual, expected);
});
describe("mergeProcessCovs", () => {
// see: https://github.com/demurgos/v8-coverage/issues/2
it("handles function coverage merged into block coverage", () => {
const blockCoverage: ProcessCov = loadFixture("issue-2-block-coverage");
const functionCoverage: ProcessCov = loadFixture("issue-2-func-coverage");
const inputs: ProcessCov[] = [
functionCoverage,
blockCoverage,
];
const expected: ProcessCov = loadFixture("issue-2-expected");
const actual: ProcessCov = mergeProcessCovs(inputs);
chai.assert.deepEqual(actual, expected);
});
// see: https://github.com/demurgos/v8-coverage/issues/2
it("handles block coverage merged into function coverage", () => {
const blockCoverage: ProcessCov = loadFixture("issue-2-block-coverage");
const functionCoverage: ProcessCov = loadFixture("issue-2-func-coverage");
const inputs: ProcessCov[] = [
blockCoverage,
functionCoverage,
];
const expected: ProcessCov = loadFixture("issue-2-expected");
const actual: ProcessCov = mergeProcessCovs(inputs);
chai.assert.deepEqual(actual, expected);
});
});
it("accepts arrays with a single item for `mergeScriptCovs`", () => {
const inputs: ScriptCov[] = [
{
scriptId: "123",
url: "/lib.js",
functions: [
{
functionName: "test",
isBlockCoverage: true,
ranges: [
{startOffset: 0, endOffset: 4, count: 2},
{startOffset: 1, endOffset: 2, count: 1},
{startOffset: 2, endOffset: 3, count: 1},
],
},
],
},
];
const expected: ScriptCov | undefined = {
scriptId: "123",
url: "/lib.js",
functions: [
{
functionName: "test",
isBlockCoverage: true,
ranges: [
{startOffset: 0, endOffset: 4, count: 2},
{startOffset: 1, endOffset: 3, count: 1},
],
},
],
};
const actual: ScriptCov | undefined = mergeScriptCovs(inputs);
chai.assert.deepEqual(actual, expected);
});
it("accepts arrays with a single item for `mergeFunctionCovs`", () => {
const inputs: FunctionCov[] = [
{
functionName: "test",
isBlockCoverage: true,
ranges: [
{startOffset: 0, endOffset: 4, count: 2},
{startOffset: 1, endOffset: 2, count: 1},
{startOffset: 2, endOffset: 3, count: 1},
],
},
];
const expected: FunctionCov = {
functionName: "test",
isBlockCoverage: true,
ranges: [
{startOffset: 0, endOffset: 4, count: 2},
{startOffset: 1, endOffset: 3, count: 1},
],
};
const actual: FunctionCov | undefined = mergeFunctionCovs(inputs);
chai.assert.deepEqual(actual, expected);
});
});
describe("ranges", () => {
for (const sourceFile of getSourceFiles()) {
const relPath: string = path.relative(RANGES_DIR, sourceFile);
describe(relPath, () => {
const content: string = fs.readFileSync(sourceFile, {encoding: "UTF-8"});
const items: MergeRangeItem[] = JSON.parse(content);
for (const item of items) {
const test: () => void = () => {
const actual: ProcessCov | undefined = mergeProcessCovs(item.inputs);
chai.assert.deepEqual(actual, item.expected);
};
switch (item.status) {
case "run":
it(item.name, test);
break;
case "only":
it.only(item.name, test);
break;
case "skip":
it.skip(item.name, test);
break;
default:
throw new Error(`Unexpected status: ${item.status}`);
}
}
});
}
});
describe("benches", () => {
for (const bench of getBenches()) {
const BENCHES_TO_SKIP: Set<string> = new Set();
if (process.env.CI === "true") {
// Skip very large benchmarks when running continuous integration
BENCHES_TO_SKIP.add("node@10.11.0");
BENCHES_TO_SKIP.add("npm@6.4.1");
}
const name: string = path.basename(bench);
if (BENCHES_TO_SKIP.has(name)) {
it.skip(`${name} (skipped: too large for CI)`, testBench);
} else {
it(name, testBench);
}
async function testBench(this: Mocha.Context) {
this.timeout(BENCHES_TIMEOUT);
const inputFileNames: string[] = await fs.promises.readdir(bench);
const inputPromises: Promise<ProcessCov>[] = [];
for (const inputFileName of inputFileNames) {
const resolved: string = path.join(bench, inputFileName);
inputPromises.push(fs.promises.readFile(resolved).then(buffer => JSON.parse(buffer.toString("UTF-8"))));
}
const inputs: ProcessCov[] = await Promise.all(inputPromises);
const expectedPath: string = path.join(BENCHES_DIR, `${name}.json`);
const expectedContent: string = await fs.promises.readFile(expectedPath, {encoding: "UTF-8"}) as string;
const expected: ProcessCov = JSON.parse(expectedContent);
const startTime: number = Date.now();
const actual: ProcessCov | undefined = mergeProcessCovs(inputs);
const endTime: number = Date.now();
console.error(`Time (${name}): ${(endTime - startTime) / 1000}`);
chai.assert.deepEqual(actual, expected);
console.error(`OK: ${name}`);
}
}
});
});
function getSourceFiles() {
return getSourcesFrom(RANGES_DIR);
function* getSourcesFrom(dir: string): Iterable<string> {
const names: string[] = fs.readdirSync(dir);
for (const name of names) {
const resolved: string = path.join(dir, name);
const stat: fs.Stats = fs.statSync(resolved);
if (stat.isDirectory()) {
yield* getSourcesFrom(dir);
} else {
yield resolved;
}
}
}
}
function* getBenches(): Iterable<string> {
const names: string[] = fs.readdirSync(BENCHES_INPUT_DIR);
for (const name of names) {
const resolved: string = path.join(BENCHES_INPUT_DIR, name);
const stat: fs.Stats = fs.statSync(resolved);
if (stat.isDirectory()) {
yield resolved;
}
}
}

View File

@ -0,0 +1,59 @@
{
"compilerOptions": {
"allowJs": false,
"allowSyntheticDefaultImports": true,
"allowUnreachableCode": false,
"allowUnusedLabels": false,
"alwaysStrict": true,
"charset": "utf8",
"checkJs": false,
"declaration": false,
"disableSizeLimit": false,
"downlevelIteration": false,
"emitBOM": false,
"emitDecoratorMetadata": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"forceConsistentCasingInFileNames": true,
"importHelpers": false,
"inlineSourceMap": false,
"inlineSources": false,
"isolatedModules": false,
"lib": [
"es2017",
"esnext.asynciterable"
],
"locale": "en-us",
"module": "commonjs",
"moduleResolution": "node",
"newLine": "lf",
"noEmit": false,
"noEmitHelpers": false,
"noEmitOnError": true,
"noErrorTruncation": true,
"noFallthroughCasesInSwitch": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noImplicitThis": true,
"noStrictGenericChecks": false,
"noUnusedLocals": true,
"noUnusedParameters": false,
"noImplicitUseStrict": false,
"noLib": false,
"noResolve": false,
"preserveConstEnums": false,
"removeComments": false,
"skipLibCheck": true,
"sourceMap": true,
"strict": true,
"strictNullChecks": true,
"suppressExcessPropertyErrors": false,
"suppressImplicitAnyIndexErrors": false,
"target": "es2017",
"traceResolution": false,
"typeRoots": [
"src/lib/custom-typings",
"node_modules/@types"
]
}
}

View File

@ -0,0 +1 @@
[1-9]*.test.js

View File

@ -0,0 +1,8 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
it('is fast', () => {});

View File

@ -0,0 +1,7 @@
First, run `./prepare.sh` to generate the benchmark files. On Windows, use a Bash (WSL, Git, Cygwin …) to do this, but you can use CMD for the actual benchmark run if the CMD environment is what you want to benchmark for.
To run the benchmark, use a benchmarking tool such as [hyperfine](https://github.com/sharkdp/hyperfine):
```sh
hyperfine -w 3 -m 10 ../../jest /tmp/other-jest-clone-to-compare-against/jest
```

View File

@ -0,0 +1,5 @@
{
"jest": {
"testEnvironment": "node"
}
}

View File

@ -0,0 +1,4 @@
#!/usr/bin/env sh
for i in {1..499}; do
cp 0.test.js $i.test.js
done

View File

@ -0,0 +1,11 @@
# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!
__metadata:
version: 4
"root-workspace-0b6124@workspace:.":
version: 0.0.0-use.local
resolution: "root-workspace-0b6124@workspace:."
languageName: unknown
linkType: soft

13
char-regex/.editorconfig Normal file
View File

@ -0,0 +1,13 @@
# editorconfig.org
root = true
[*]
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
indent_style = tab
[*.md]
trim_trailing_whitespace = false

76
char-regex/.github/CODE_OF_CONDUCT.md vendored Normal file
View File

@ -0,0 +1,76 @@
# Contributor Covenant Code of Conduct
## Our Pledge
In the interest of fostering an open and welcoming environment, we as
contributors and maintainers pledge to making participation in our project and
our community a harassment-free experience for everyone, regardless of age, body
size, disability, ethnicity, sex characteristics, gender identity and expression,
level of experience, education, socio-economic status, nationality, personal
appearance, race, religion, or sexual identity and orientation.
## Our Standards
Examples of behavior that contributes to creating a positive environment
include:
- Using welcoming and inclusive language
- Being respectful of differing viewpoints and experiences
- Gracefully accepting constructive criticism
- Focusing on what is best for the community
- Showing empathy towards other community members
Examples of unacceptable behavior by participants include:
- The use of sexualized language or imagery and unwelcome sexual attention or
advances
- Trolling, insulting/derogatory comments, and personal or political attacks
- Public or private harassment
- Publishing others' private information, such as a physical or electronic
address, without explicit permission
- Other conduct which could reasonably be considered inappropriate in a
professional setting
## Our Responsibilities
Project maintainers are responsible for clarifying the standards of acceptable
behavior and are expected to take appropriate and fair corrective action in
response to any instances of unacceptable behavior.
Project maintainers have the right and responsibility to remove, edit, or
reject comments, commits, code, wiki edits, issues, and other contributions
that are not aligned to this Code of Conduct, or to ban temporarily or
permanently any contributor for other behaviors that they deem inappropriate,
threatening, offensive, or harmful.
## Scope
This Code of Conduct applies both within project spaces and in public spaces
when an individual is representing the project or its community. Examples of
representing a project or community include using an official project e-mail
address, posting via an official social media account, or acting as an appointed
representative at an online or offline event. Representation of a project may be
further defined and clarified by project maintainers.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported by contacting the project team at richiebendall@gmail.com. All
complaints will be reviewed and investigated and will result in a response that
is deemed necessary and appropriate to the circumstances. The project team is
obligated to maintain confidentiality with regard to the reporter of an incident.
Further details of specific enforcement policies may be posted separately.
Project maintainers who do not follow or enforce the Code of Conduct in good
faith may face temporary or permanent repercussions as determined by other
members of the project's leadership.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see
https://www.contributor-covenant.org/faq

12
char-regex/.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1,12 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: richienb # Replace with a single Patreon username
open_collective: richie-bendall # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: richienb # Replace with a single Liberapay username
issuehunt: richienb # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']

View File

@ -0,0 +1,40 @@
---
name: Bug report
about: Create a report to help us improve
title: ""
labels: ""
assignees: ""
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,19 @@
---
name: Feature request
about: Suggest an idea for this project
title: ""
labels: ""
assignees: ""
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

8
char-regex/.travis.yml Normal file
View File

@ -0,0 +1,8 @@
language: node_js
cache:
- yarn
node_js:
- "lts/*"
- "node"

21
char-regex/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Richie Bendall
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.

27
char-regex/README.md Normal file
View File

@ -0,0 +1,27 @@
# Char Regex [![Travis CI Build Status](https://img.shields.io/travis/com/Richienb/char-regex/master.svg?style=for-the-badge)](https://travis-ci.com/Richienb/char-regex)
A regex to match any full character, considering weird character ranges. Tested on every single emoji and unicode character. Based on the Lodash implementation.
[![NPM Badge](https://nodei.co/npm/char-regex.png)](https://npmjs.com/package/char-regex)
## Install
```sh
npm install char-regex
```
## Usage
```js
const charRegex = require("char-regex");
"❤️👊🏽".match(/./);
//=> ["", "", "", "", "", "", ""]
"❤️👊🏽".match(charRegex());
//=> ["❤️", "👊🏽"]
```
## API
### charRegex()

13
char-regex/index.d.ts vendored Normal file
View File

@ -0,0 +1,13 @@
/**
* A regex to match any full character, considering weird character ranges.
* @example
* ```
* const charRegex = require("char-regex");
*
* "❤️👊🏽".match(charRegex());
* //=> ["❤️", "👊🏽"]
* ```
*/
declare function charRegex(): RegExp
export = charRegex

39
char-regex/index.js Normal file
View File

@ -0,0 +1,39 @@
"use strict"
// Based on: https://github.com/lodash/lodash/blob/6018350ac10d5ce6a5b7db625140b82aeab804df/.internal/unicodeSize.js
module.exports = () => {
// Used to compose unicode character classes.
const astralRange = "\\ud800-\\udfff"
const comboMarksRange = "\\u0300-\\u036f"
const comboHalfMarksRange = "\\ufe20-\\ufe2f"
const comboSymbolsRange = "\\u20d0-\\u20ff"
const comboMarksExtendedRange = "\\u1ab0-\\u1aff"
const comboMarksSupplementRange = "\\u1dc0-\\u1dff"
const comboRange = comboMarksRange + comboHalfMarksRange + comboSymbolsRange + comboMarksExtendedRange + comboMarksSupplementRange
const varRange = "\\ufe0e\\ufe0f"
const familyRange = "\\uD83D\\uDC69\\uD83C\\uDFFB\\u200D\\uD83C\\uDF93"
// Used to compose unicode capture groups.
const astral = `[${astralRange}]`
const combo = `[${comboRange}]`
const fitz = "\\ud83c[\\udffb-\\udfff]"
const modifier = `(?:${combo}|${fitz})`
const nonAstral = `[^${astralRange}]`
const regional = "(?:\\uD83C[\\uDDE6-\\uDDFF]){2}"
const surrogatePair = "[\\ud800-\\udbff][\\udc00-\\udfff]"
const zwj = "\\u200d"
const blackFlag = "(?:\\ud83c\\udff4\\udb40\\udc67\\udb40\\udc62\\udb40(?:\\udc65|\\udc73|\\udc77)\\udb40(?:\\udc6e|\\udc63|\\udc6c)\\udb40(?:\\udc67|\\udc74|\\udc73)\\udb40\\udc7f)"
const family = `[${familyRange}]`
// Used to compose unicode regexes.
const optModifier = `${modifier}?`
const optVar = `[${varRange}]?`
const optJoin = `(?:${zwj}(?:${[nonAstral, regional, surrogatePair].join("|")})${optVar + optModifier})*`
const seq = optVar + optModifier + optJoin
const nonAstralCombo = `${nonAstral}${combo}?`
const symbol = `(?:${[nonAstralCombo, combo, regional, surrogatePair, astral, family].join("|")})`
// Used to match [String symbols](https://mathiasbynens.be/notes/javascript-unicode).
return new RegExp(`${blackFlag}|${fitz}(?=${fitz})|${symbol + seq}`, "g")
}

44
char-regex/package.json Normal file
View File

@ -0,0 +1,44 @@
{
"name": "char-regex",
"version": "1.0.2",
"description": "A regex to match any full character, considering weird character ranges.",
"repository": "https://github.com/Richienb/char-regex.git",
"author": "Richie Bendall <richiebendall@gmail.com>",
"license": "MIT",
"main": "index.js",
"files": [
"index.js",
"index.d.ts"
],
"engines": {
"node": ">=10"
},
"scripts": {
"lint": "xo",
"test": "yarn lint && ava"
},
"keywords": [
"character",
"regex",
"match",
"split",
"length"
],
"dependencies": {},
"devDependencies": {
"@babel/core": "^7.8.4",
"@babel/plugin-proposal-unicode-property-regex": "^7.8.3",
"array-uniq": "^2.1.0",
"ava": "^3.0.0",
"emoji.json": "^12.1.1",
"eslint-config-richienb": "^0.3.0",
"unicode-chars": "^1.0.1",
"xo": "^0.25.3"
},
"resolutions": {
"eslint": "^6.8.0"
},
"xo": {
"extends": "richienb/node"
}
}

17
char-regex/test.js Normal file
View File

@ -0,0 +1,17 @@
const test = require("ava")
const arrayUniq = require("array-uniq")
const unicodeChars = arrayUniq([...require("unicode-chars")(), ...require("emoji.json").map(({ char }) => char)])
const charRegex = require(".")()
// See: https://mathiasbynens.be/notes/javascript-unicode#poo-test
test("The Pile of Poo Test™", (t) => {
t.deepEqual("Iñtërnâtiônàlizætiøn☃💩".match(charRegex), [
"I", "ñ", "t", "ë", "r", "n", "â", "t", "i", "ô", "n", "à", "l", "i", "z", "æ", "t", "i", "ø", "n", "☃", "💩",
])
})
unicodeChars.forEach((character) => {
test(`Test "${character}"`, (t) => {
t.deepEqual(character.match(charRegex), [character])
})
})

4590
char-regex/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

10
cjs-module-lexer/.babelrc Executable file
View File

@ -0,0 +1,10 @@
{
"plugins": [
[
"@babel/plugin-transform-modules-commonjs",
{
"strict": true
},
]
]
}

View File

@ -0,0 +1,30 @@
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: Node.js CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [14.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm run build --if-present
- run: npm test

3
cjs-module-lexer/.gitignore vendored Executable file
View File

@ -0,0 +1,3 @@
node_modules
dist
yarn.lock

View File

@ -0,0 +1,12 @@
git:
depth: 1
language: node_js
node_js:
- '13'
before_install:
- npm install
script:
- npm run build
- npm run test

View File

@ -0,0 +1,40 @@
1.2.2
- Fix RollupJS reexports bug (https://github.com/guybedford/cjs-module-lexer/pull/59)
1.2.1
- Support Unicode escapes in strings (https://github.com/guybedford/cjs-module-lexer/pull/55)
- Filter export strings to valid surrogate pairs (https://github.com/guybedford/cjs-module-lexer/pull/56)
1.2.0
- Support for non-identifier exports (https://github.com/guybedford/cjs-module-lexer/pull/54, @nicolo-ribaudo)
1.1.1
- Better support for Babel reexport getter function forms (https://github.com/guybedford/cjs-module-lexer/issues/50)
- Support Babel interopRequireWildcard reexports patterns (https://github.com/guybedford/cjs-module-lexer/issues/52)
1.1.0
- Support for Babel reexport conflict filter (https://github.com/guybedford/cjs-module-lexer/issues/36, @nicolo-ribaudo)
- Support trailing commas in getter patterns (https://github.com/guybedford/cjs-module-lexer/issues/31)
- Support for RollupJS reexports property checks (https://github.com/guybedford/cjs-module-lexer/issues/38)
1.0.0
- Unsafe getter tracking (https://github.com/guybedford/cjs-module-lexer/pull/29)
0.6.0
- API-only breaking change: Unify JS and Wasm interfaces (https://github.com/guybedford/cjs-module-lexer/pull/27)
- Add type definitions (https://github.com/guybedford/cjs-module-lexer/pull/28)
0.5.2
- Support named getter functions (https://github.com/guybedford/cjs-module-lexer/pull/26)
0.5.1:
- Feature: Implement specific reexport getter forms (https://github.com/guybedford/cjs-module-lexer/pull/25)
0.5.0
- Breaking Change: No longer emit Object.defineProperty exports (https://github.com/guybedford/cjs-module-lexer/pull/24)
- Doc: Update link to WASI SDK (https://github.com/guybedford/cjs-module-lexer/pull/19)
0.4.3
- Support for Babel 7.12 reexports (https://github.com/guybedford/cjs-module-lexer/pull/16)
- Support module.exports = { ...require('x') } reexports (https://github.com/guybedford/cjs-module-lexer/pull/18)
- "if" keyword space parsing in exports matching (https://github.com/guybedford/cjs-module-lexer/pull/17)

10
cjs-module-lexer/LICENSE Executable file
View File

@ -0,0 +1,10 @@
MIT License
-----------
Copyright (C) 2018-2020 Guy Bedford
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.

16
cjs-module-lexer/Makefile Executable file
View File

@ -0,0 +1,16 @@
lslib/lexer.wat: lib/lexer.wasm
../wabt/bin/wasm2wat lib/lexer.wasm -o lib/lexer.wat
lib/lexer.wasm: include-wasm/cjs-module-lexer.h src/lexer.c
@mkdir -p lib
../wasi-sdk-11.0/bin/clang src/lexer.c -I include-wasm --sysroot=../wasi-sdk-11.0/share/wasi-sysroot -o lib/lexer.wasm -nostartfiles \
-Wl,-z,stack-size=13312,--no-entry,--compress-relocations,--strip-all,--export=__heap_base,\
--export=parseCJS,--export=sa,--export=e,--export=re,--export=es,--export=ee,--export=rre,--export=ree,--export=res,--export=ru,--export=us,--export=ue \
-Wno-logical-op-parentheses -Wno-parentheses \
-Oz
optimize: lib/lexer.wasm
../binaryen/bin/wasm-opt -Oz lib/lexer.wasm -o lib/lexer.wasm
clean:
rm lib/*

453
cjs-module-lexer/README.md Executable file
View File

@ -0,0 +1,453 @@
# CJS Module Lexer
[![Build Status][travis-image]][travis-url]
A [very fast](#benchmarks) JS CommonJS module syntax lexer used to detect the most likely list of named exports of a CommonJS module.
Outputs the list of named exports (`exports.name = ...`) and possible module reexports (`module.exports = require('...')`), including the common transpiler variations of these cases.
Forked from https://github.com/guybedford/es-module-lexer.
_Comprehensively handles the JS language grammar while remaining small and fast. - ~90ms per MB of JS cold and ~15ms per MB of JS warm, [see benchmarks](#benchmarks) for more info._
### Usage
```
npm install cjs-module-lexer
```
For use in CommonJS:
```js
const { parse } = require('cjs-module-lexer');
// `init` return a promise for parity with the ESM API, but you do not have to call it
const { exports, reexports } = parse(`
// named exports detection
module.exports.a = 'a';
(function () {
exports.b = 'b';
})();
Object.defineProperty(exports, 'c', { value: 'c' });
/* exports.d = 'not detected'; */
// reexports detection
if (maybe) module.exports = require('./dep1.js');
if (another) module.exports = require('./dep2.js');
// literal exports assignments
module.exports = { a, b: c, d, 'e': f }
// __esModule detection
Object.defineProperty(module.exports, '__esModule', { value: true })
`);
// exports === ['a', 'b', 'c', '__esModule']
// reexports === ['./dep1.js', './dep2.js']
```
When using the ESM version, Wasm is supported instead:
```js
import { parse, init } from 'cjs-module-lexer';
// init needs to be called and waited upon
await init();
const { exports, reexports } = parse(source);
```
The Wasm build is around 1.5x faster and without a cold start.
### Grammar
CommonJS exports matches are run against the source token stream.
The token grammar is:
```
IDENTIFIER: As defined by ECMA-262, without support for identifier `\` escapes, filtered to remove strict reserved words:
"implements", "interface", "let", "package", "private", "protected", "public", "static", "yield", "enum"
STRING_LITERAL: A `"` or `'` bounded ECMA-262 string literal.
MODULE_EXPORTS: `module` `.` `exports`
EXPORTS_IDENTIFIER: MODULE_EXPORTS_IDENTIFIER | `exports`
EXPORTS_DOT_ASSIGN: EXPORTS_IDENTIFIER `.` IDENTIFIER `=`
EXPORTS_LITERAL_COMPUTED_ASSIGN: EXPORTS_IDENTIFIER `[` STRING_LITERAL `]` `=`
EXPORTS_LITERAL_PROP: (IDENTIFIER (`:` IDENTIFIER)?) | (STRING_LITERAL `:` IDENTIFIER)
EXPORTS_SPREAD: `...` (IDENTIFIER | REQUIRE)
EXPORTS_MEMBER: EXPORTS_DOT_ASSIGN | EXPORTS_LITERAL_COMPUTED_ASSIGN
EXPORTS_DEFINE: `Object` `.` `defineProperty `(` EXPORTS_IDENFITIER `,` STRING_LITERAL
EXPORTS_DEFINE_VALUE: EXPORTS_DEFINE `, {`
(`enumerable: true,`)?
(
`value:` |
`get` (`: function` IDENTIFIER? )? `() {` return IDENTIFIER (`.` IDENTIFIER | `[` STRING_LITERAL `]`)? `;`? `}` `,`?
)
`})`
EXPORTS_LITERAL: MODULE_EXPORTS `=` `{` (EXPORTS_LITERAL_PROP | EXPORTS_SPREAD) `,`)+ `}`
REQUIRE: `require` `(` STRING_LITERAL `)`
EXPORTS_ASSIGN: (`var` | `const` | `let`) IDENTIFIER `=` (`_interopRequireWildcard (`)? REQUIRE
MODULE_EXPORTS_ASSIGN: MODULE_EXPORTS `=` REQUIRE
EXPORT_STAR: (`__export` | `__exportStar`) `(` REQUIRE
EXPORT_STAR_LIB: `Object.keys(` IDENTIFIER$1 `).forEach(function (` IDENTIFIER$2 `) {`
(
(
`if (` IDENTIFIER$2 `===` ( `'default'` | `"default"` ) `||` IDENTIFIER$2 `===` ( '__esModule' | `"__esModule"` ) `) return` `;`?
(
(`if (Object` `.prototype`? `.hasOwnProperty.call(` IDENTIFIER `, ` IDENTIFIER$2 `)) return` `;`?)?
(`if (` IDENTIFIER$2 `in` EXPORTS_IDENTIFIER `&&` EXPORTS_IDENTIFIER `[` IDENTIFIER$2 `] ===` IDENTIFIER$1 `[` IDENTIFIER$2 `]) return` `;`)?
)?
) |
`if (` IDENTIFIER$2 `!==` ( `'default'` | `"default"` ) (`&& !` (`Object` `.prototype`? `.hasOwnProperty.call(` IDENTIFIER `, ` IDENTIFIER$2 `)` | IDENTIFIER `.hasOwnProperty(` IDENTIFIER$2 `)`))? `)`
)
(
EXPORTS_IDENTIFIER `[` IDENTIFIER$2 `] =` IDENTIFIER$1 `[` IDENTIFIER$2 `]` `;`? |
`Object.defineProperty(` EXPORTS_IDENTIFIER `, ` IDENTIFIER$2 `, { enumerable: true, get` (`: function` IDENTIFIER? )? `() { return ` IDENTIFIER$1 `[` IDENTIFIER$2 `]` `;`? `}` `,`? `})` `;`?
)
`})`
```
Spacing between tokens is taken to be any ECMA-262 whitespace, ECMA-262 block comment or ECMA-262 line comment.
* The returned export names are taken to be the combination of:
1. All `IDENTIFIER` and `STRING_LITERAL` slots for `EXPORTS_MEMBER` and `EXPORTS_LITERAL` matches.
2. The first `STRING_LITERAL` slot for all `EXPORTS_DEFINE_VALUE` matches where that same string is not an `EXPORTS_DEFINE` match that is not also an `EXPORTS_DEFINE_VALUE` match.
* The reexport specifiers are taken to be the combination of:
1. The `REQUIRE` matches of the last matched of either `MODULE_EXPORTS_ASSIGN` or `EXPORTS_LITERAL`.
2. All _top-level_ `EXPORT_STAR` `REQUIRE` matches and `EXPORTS_ASSIGN` matches whose `IDENTIFIER` also matches the first `IDENTIFIER` in `EXPORT_STAR_LIB`.
### Parsing Examples
#### Named Exports Parsing
The basic matching rules for named exports are `exports.name`, `exports['name']` or `Object.defineProperty(exports, 'name', ...)`. This matching is done without scope analysis and regardless of the expression position:
```js
// DETECTS EXPORTS: a, b
(function (exports) {
exports.a = 'a';
exports['b'] = 'b';
})(exports);
```
Because there is no scope analysis, the above detection may overclassify:
```js
// DETECTS EXPORTS: a, b, c
(function (exports, Object) {
exports.a = 'a';
exports['b'] = 'b';
if (false)
exports.c = 'c';
})(NOT_EXPORTS, NOT_OBJECT);
```
It will in turn underclassify in cases where the identifiers are renamed:
```js
// DETECTS: NO EXPORTS
(function (e) {
e.a = 'a';
e['b'] = 'b';
})(exports);
```
#### Getter Exports Parsing
`Object.defineProperty` is detected for specifically value and getter forms returning an identifier or member expression:
```js
// DETECTS: a, b, c, d, __esModule
Object.defineProperty(exports, 'a', {
enumerable: true,
get: function () {
return q.p;
}
});
Object.defineProperty(exports, 'b', {
enumerable: true,
get: function () {
return q['p'];
}
});
Object.defineProperty(exports, 'c', {
enumerable: true,
get () {
return b;
}
});
Object.defineProperty(exports, 'd', { value: 'd' });
Object.defineProperty(exports, '__esModule', { value: true });
```
Value properties are also detected specifically:
```js
Object.defineProperty(exports, 'a', {
value: 'no problem'
});
```
To avoid matching getters that have side effects, any getter for an export name that does not support the forms above will
opt-out of the getter matching:
```js
// DETECTS: NO EXPORTS
Object.defineProperty(exports, 'a', {
get () {
return 'nope';
}
});
if (false) {
Object.defineProperty(module.exports, 'a', {
get () {
return dynamic();
}
})
}
```
Alternative object definition structures or getter function bodies are not detected:
```js
// DETECTS: NO EXPORTS
Object.defineProperty(exports, 'a', {
enumerable: false,
get () {
return p;
}
});
Object.defineProperty(exports, 'b', {
configurable: true,
get () {
return p;
}
});
Object.defineProperty(exports, 'c', {
get: () => p
});
Object.defineProperty(exports, 'd', {
enumerable: true,
get: function () {
return dynamic();
}
});
Object.defineProperty(exports, 'e', {
enumerable: true,
get () {
return 'str';
}
});
```
`Object.defineProperties` is also not supported.
#### Exports Object Assignment
A best-effort is made to detect `module.exports` object assignments, but because this is not a full parser, arbitrary expressions are not handled in the
object parsing process.
Simple object definitions are supported:
```js
// DETECTS EXPORTS: a, b, c
module.exports = {
a,
'b': b,
c: c,
...d
};
```
Object properties that are not identifiers or string expressions will bail out of the object detection, while spreads are ignored:
```js
// DETECTS EXPORTS: a, b
module.exports = {
a,
...d,
b: require('c'),
c: "not detected since require('c') above bails the object detection"
}
```
`Object.defineProperties` is not currently supported either.
#### module.exports reexport assignment
Any `module.exports = require('mod')` assignment is detected as a reexport, but only the last one is returned:
```js
// DETECTS REEXPORTS: c
module.exports = require('a');
(module => module.exports = require('b'))(NOT_MODULE);
if (false) module.exports = require('c');
```
This is to avoid over-classification in Webpack bundles with externals which include `module.exports = require('external')` in their source for every external dependency.
In exports object assignment, any spread of `require()` are detected as multiple separate reexports:
```js
// DETECTS REEXPORTS: a, b
module.exports = require('ignored');
module.exports = {
...require('a'),
...require('b')
};
```
#### Transpiler Re-exports
For named exports, transpiler output works well with the rules described above.
But for star re-exports, special care is taken to support common patterns of transpiler outputs from Babel and TypeScript as well as bundlers like RollupJS.
These reexport and star reexport patterns are restricted to only be detected at the top-level as provided by the direct output of these tools.
For example, `export * from 'external'` is output by Babel as:
```js
"use strict";
exports.__esModule = true;
var _external = require("external");
Object.keys(_external).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
exports[key] = _external[key];
});
```
Where the `var _external = require("external")` is specifically detected as well as the `Object.keys(_external)` statement, down to the exact
for of that entire expression including minor variations of the output. The `_external` and `key` identifiers are carefully matched in this
detection.
Similarly for TypeScript, `export * from 'external'` is output as:
```js
"use strict";
function __export(m) {
for (var p in m) if (!exports.hasOwnProperty(p)) exports[p] = m[p];
}
Object.defineProperty(exports, "__esModule", { value: true });
__export(require("external"));
```
Where the `__export(require("external"))` statement is explicitly detected as a reexport, including variations `tslib.__export` and `__exportStar`.
### Environment Support
Node.js 10+, and [all browsers with Web Assembly support](https://caniuse.com/#feat=wasm).
### JS Grammar Support
* Token state parses all line comments, block comments, strings, template strings, blocks, parens and punctuators.
* Division operator / regex token ambiguity is handled via backtracking checks against punctuator prefixes, including closing brace or paren backtracking.
* Always correctly parses valid JS source, but may parse invalid JS source without errors.
### Benchmarks
Benchmarks can be run with `npm run bench`.
Current results:
JS Build:
```
Module load time
> 4ms
Cold Run, All Samples
test/samples/*.js (3635 KiB)
> 299ms
Warm Runs (average of 25 runs)
test/samples/angular.js (1410 KiB)
> 13.96ms
test/samples/angular.min.js (303 KiB)
> 4.72ms
test/samples/d3.js (553 KiB)
> 6.76ms
test/samples/d3.min.js (250 KiB)
> 4ms
test/samples/magic-string.js (34 KiB)
> 0.64ms
test/samples/magic-string.min.js (20 KiB)
> 0ms
test/samples/rollup.js (698 KiB)
> 8.48ms
test/samples/rollup.min.js (367 KiB)
> 5.36ms
Warm Runs, All Samples (average of 25 runs)
test/samples/*.js (3635 KiB)
> 40.28ms
```
Wasm Build:
```
Module load time
> 10ms
Cold Run, All Samples
test/samples/*.js (3635 KiB)
> 43ms
Warm Runs (average of 25 runs)
test/samples/angular.js (1410 KiB)
> 9.32ms
test/samples/angular.min.js (303 KiB)
> 3.16ms
test/samples/d3.js (553 KiB)
> 5ms
test/samples/d3.min.js (250 KiB)
> 2.32ms
test/samples/magic-string.js (34 KiB)
> 0.16ms
test/samples/magic-string.min.js (20 KiB)
> 0ms
test/samples/rollup.js (698 KiB)
> 6.28ms
test/samples/rollup.min.js (367 KiB)
> 3.6ms
Warm Runs, All Samples (average of 25 runs)
test/samples/*.js (3635 KiB)
> 27.76ms
```
### Wasm Build Steps
To build download the WASI SDK from https://github.com/WebAssembly/wasi-sdk/releases.
The Makefile assumes the existence of "wasi-sdk-11.0" and "wabt" (optional) as sibling folders to this project.
The build through the Makefile is then run via `make lib/lexer.wasm`, which can also be triggered via `npm run build-wasm` to create `dist/lexer.js`.
On Windows it may be preferable to use the Linux subsystem.
After the Web Assembly build, the CJS build can be triggered via `npm run build`.
Optimization passes are run with [Binaryen](https://github.com/WebAssembly/binaryen) prior to publish to reduce the Web Assembly footprint.
### License
MIT
[travis-url]: https://travis-ci.org/guybedford/es-module-lexer
[travis-image]: https://travis-ci.org/guybedford/es-module-lexer.svg?branch=master

View File

@ -0,0 +1,90 @@
/*
* Shimport benchmarks for comparison
*/
import fs from 'fs';
import c from 'kleur';
const n = 25;
const files = fs.readdirSync('test/samples')
.map(f => `test/samples/${f}`)
.filter(x => x.endsWith('.js'))
.map(file => {
const source = fs.readFileSync(file);
return {
file,
code: source.toString(),
size: source.byteLength
};
});
Promise.resolve().then(async () => {
function timeRun (code) {
const start = process.hrtime.bigint();
const parsed = parse(code);
const end = process.hrtime.bigint();
return Math.round(Number(end - start) / 1e6);
}
console.log('--- JS Build ---');
console.log('Module load time');
{
const start = process.hrtime.bigint();
var { parse } = await import('../lexer.js');
console.log(`> ${c.bold.green(Math.round(Number(process.hrtime.bigint() - start) / 1e6) + 'ms')}`);
}
doRun();
console.log('--- Wasm Build ---');
console.log('Module load time');
{
const start = process.hrtime.bigint();
var { parse, init } = await import('../dist/lexer.mjs');
await init();
console.log(`> ${c.bold.green(Math.round(Number(process.hrtime.bigint() - start) / 1e6) + 'ms')}`);
}
doRun();
function doRun () {
console.log('Cold Run, All Samples');
let totalSize = 0;
{
let total = 0;
files.forEach(({ code, size }) => {
totalSize += size;
total += timeRun(code);
});
console.log(c.bold.cyan(`test/samples/*.js (${Math.round(totalSize / 1e3)} KiB)`));
console.log(`> ${c.bold.green(total + 'ms')}`);
gc();
}
console.log(`\nWarm Runs (average of ${n} runs)`);
files.forEach(({ file, code, size }) => {
console.log(c.bold.cyan(`${file} (${Math.round(size / 1e3)} KiB)`));
let total = 0;
for (let i = 0; i < n; i++) {
total += timeRun(code);
gc();
}
console.log(`> ${c.bold.green((total / n) + 'ms')}`);
});
console.log(`\nWarm Runs, All Samples (average of ${n} runs)`);
{
let total = 0;
for (let i = 0; i < n; i++) {
files.forEach(({ code }) => {
total += timeRun(code);
});
}
console.log(c.bold.cyan(`test/samples/*.js (${Math.round(totalSize / 1e3)} KiB)`));
console.log(`> ${c.bold.green((total / n) + 'ms')}`);
}
}
});

22
cjs-module-lexer/build.js Executable file
View File

@ -0,0 +1,22 @@
const fs = require('fs');
const terser = require('terser');
const MINIFY = true;
try { fs.mkdirSync('./dist'); }
catch (e) {}
const wasmBuffer = fs.readFileSync('./lib/lexer.wasm');
const jsSource = fs.readFileSync('./src/lexer.js').toString();
const pjson = JSON.parse(fs.readFileSync('./package.json').toString());
const jsSourceProcessed = jsSource.replace('WASM_BINARY', wasmBuffer.toString('base64'));
const minified = MINIFY && terser.minify(jsSourceProcessed, {
module: true,
output: {
preamble: `/* cjs-module-lexer ${pjson.version} */`
}
});
fs.writeFileSync('./dist/lexer.mjs', minified ? minified.code : jsSourceProcessed);

View File

@ -0,0 +1,240 @@
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <limits.h>
#include <string.h>
extern unsigned char __heap_base;
const uint16_t* source = (void*)&__heap_base;
uint32_t parse_error;
struct Slice {
const uint16_t* start;
const uint16_t* end;
struct Slice* next;
};
typedef struct Slice Slice;
struct StarExportBinding {
const uint16_t* specifier_start;
const uint16_t* specifier_end;
const uint16_t* id_start;
const uint16_t* id_end;
};
typedef struct StarExportBinding StarExportBinding;
Slice* first_export = NULL;
Slice* export_read_head = NULL;
Slice* export_write_head = NULL;
Slice* first_reexport = NULL;
Slice* reexport_read_head = NULL;
Slice* reexport_write_head = NULL;
Slice* first_unsafe_getter = NULL;
Slice* unsafe_getter_read_head = NULL;
Slice* unsafe_getter_write_head = NULL;
void* analysis_base;
void* analysis_head;
void bail (uint32_t err);
// allocateSource
const uint16_t* sa (uint32_t utf16Len) {
const uint16_t* sourceEnd = source + utf16Len + 1;
// ensure source is null terminated
*(uint16_t*)(source + utf16Len) = '\0';
analysis_base = (void*)sourceEnd;
analysis_head = analysis_base;
first_export = NULL;
export_write_head = NULL;
export_read_head = NULL;
first_reexport = NULL;
reexport_write_head = NULL;
reexport_read_head = NULL;
first_unsafe_getter = NULL;
unsafe_getter_write_head = NULL;
unsafe_getter_read_head = NULL;
return source;
}
// getErr
uint32_t e () {
return parse_error;
}
// getExportStart
uint32_t es () {
return export_read_head->start - source;
}
// getExportEnd
uint32_t ee () {
return export_read_head->end - source;
}
// getReexportStart
uint32_t res () {
return reexport_read_head->start - source;
}
// getReexportEnd
uint32_t ree () {
return reexport_read_head->end - source;
}
// getUnsafeGetterStart
uint32_t us () {
return unsafe_getter_read_head->start - source;
}
// getUnsafeGetterEnd
uint32_t ue () {
return unsafe_getter_read_head->end - source;
}
// readExport
bool re () {
if (export_read_head == NULL)
export_read_head = first_export;
else
export_read_head = export_read_head->next;
if (export_read_head == NULL)
return false;
return true;
}
// readReexport
bool rre () {
if (reexport_read_head == NULL)
reexport_read_head = first_reexport;
else
reexport_read_head = reexport_read_head->next;
if (reexport_read_head == NULL)
return false;
return true;
}
// readUnsafeGetter
bool ru () {
if (unsafe_getter_read_head == NULL)
unsafe_getter_read_head = first_unsafe_getter;
else
unsafe_getter_read_head = unsafe_getter_read_head->next;
if (unsafe_getter_read_head == NULL)
return false;
return true;
}
bool parse (uint32_t point);
void _addExport (const uint16_t* start, const uint16_t* end) {
Slice* export = (Slice*)(analysis_head);
analysis_head = analysis_head + sizeof(Slice);
if (export_write_head == NULL)
first_export = export;
else
export_write_head->next = export;
export_write_head = export;
export->start = start;
export->end = end;
export->next = NULL;
}
void _addReexport (const uint16_t* start, const uint16_t* end) {
Slice* reexport = (Slice*)(analysis_head);
analysis_head = analysis_head + sizeof(Slice);
if (reexport_write_head == NULL)
first_reexport = reexport;
else
reexport_write_head->next = reexport;
reexport_write_head = reexport;
reexport->start = start;
reexport->end = end;
reexport->next = NULL;
}
void _addUnsafeGetter (const uint16_t* start, const uint16_t* end) {
Slice* unsafe_getter = (Slice*)(analysis_head);
analysis_head = analysis_head + sizeof(Slice);
if (unsafe_getter_write_head == NULL)
first_unsafe_getter = unsafe_getter;
else
unsafe_getter_write_head->next = unsafe_getter;
unsafe_getter_write_head = unsafe_getter;
unsafe_getter->start = start;
unsafe_getter->end = end;
unsafe_getter->next = NULL;
}
void _clearReexports () {
reexport_write_head = NULL;
first_reexport = NULL;
}
void (*addExport)(const uint16_t*, const uint16_t*) = &_addExport;
void (*addReexport)(const uint16_t*, const uint16_t*) = &_addReexport;
void (*addUnsafeGetter)(const uint16_t*, const uint16_t*) = &_addUnsafeGetter;
void (*clearReexports)() = &_clearReexports;
bool parseCJS (uint16_t* source, uint32_t sourceLen, void (*addExport)(const uint16_t* start, const uint16_t* end), void (*addReexport)(const uint16_t* start, const uint16_t* end), void (*addUnsafeGetter)(const uint16_t*, const uint16_t*), void (*clearReexports)());
enum RequireType {
Import,
ExportAssign,
ExportStar
};
void tryBacktrackAddStarExportBinding (uint16_t* pos);
bool tryParseRequire (enum RequireType requireType);
void tryParseLiteralExports ();
bool readExportsOrModuleDotExports (uint16_t ch);
void tryParseModuleExportsDotAssign ();
void tryParseExportsDotAssign (bool assign);
void tryParseObjectDefineOrKeys (bool keys);
bool identifier (uint16_t ch);
void throwIfImportStatement ();
void throwIfExportStatement ();
void readImportString (const uint16_t* ss, uint16_t ch);
uint16_t readExportAs (uint16_t* startPos, uint16_t* endPos);
uint16_t commentWhitespace ();
void stringLiteral (uint16_t quote);
void regularExpression ();
void templateString ();
void blockComment ();
void lineComment ();
uint16_t readToWsOrPunctuator (uint16_t ch);
uint32_t fullCharCode (uint16_t ch);
uint32_t fullCharCodeAtLast (uint16_t* pos);
bool isIdentifierStart (uint32_t code);
bool isIdentifierChar (uint32_t code);
int charCodeByteLen (uint32_t ch);
bool isBr (uint16_t c);
bool isBrOrWs (uint16_t c);
bool isBrOrWsOrPunctuator (uint16_t c);
bool isBrOrWsOrPunctuatorNotDot (uint16_t c);
bool str_eq2 (uint16_t* pos, uint16_t c1, uint16_t c2);
bool str_eq3 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3);
bool str_eq4 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4);
bool str_eq5 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5);
bool str_eq6 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6);
bool str_eq7 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6, uint16_t c7);
bool str_eq8 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6, uint16_t c7, uint16_t c8);
bool str_eq9 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6, uint16_t c7, uint16_t c8, uint16_t c9);
bool str_eq10 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6, uint16_t c7, uint16_t c8, uint16_t c9, uint16_t c10);
bool str_eq13 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6, uint16_t c7, uint16_t c8, uint16_t c9, uint16_t c10, uint16_t c11, uint16_t c12, uint16_t c13);
bool str_eq18 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6, uint16_t c7, uint16_t c8, uint16_t c9, uint16_t c10, uint16_t c11, uint16_t c12, uint16_t c13, uint16_t c14, uint16_t c15, uint16_t c16, uint16_t c17, uint16_t c18);
bool str_eq22 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6, uint16_t c7, uint16_t c8, uint16_t c9, uint16_t c10, uint16_t c11, uint16_t c12, uint16_t c13, uint16_t c14, uint16_t c15, uint16_t c16, uint16_t c17, uint16_t c18, uint16_t c19, uint16_t c20, uint16_t c21, uint16_t c22);
bool readPrecedingKeyword2(uint16_t* pos, uint16_t c1, uint16_t c2);
bool readPrecedingKeyword3(uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3);
bool readPrecedingKeyword4(uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4);
bool readPrecedingKeyword5(uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5);
bool readPrecedingKeyword6(uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6);
bool readPrecedingKeyword7(uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6, uint16_t c7);
bool keywordStart (uint16_t* pos);
bool isExpressionKeyword (uint16_t* pos);
bool isParenKeyword (uint16_t* pos);
bool isPunctuator (uint16_t charCode);
bool isExpressionPunctuator (uint16_t charCode);
bool isExpressionTerminator (uint16_t* pos);
void nextChar (uint16_t ch);
void nextCharSurrogate (uint16_t ch);
uint16_t readChar ();
void syntaxError ();

View File

@ -0,0 +1,108 @@
#ifndef __CJS_MODULE_LEXER_H__
#define __CJS_MODULE_LEXER_H__
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdio.h>
#include <string.h>
struct Slice {
const uint16_t* start;
const uint16_t* end;
};
typedef struct Slice Slice;
struct StarExportBinding {
const uint16_t* specifier_start;
const uint16_t* specifier_end;
const uint16_t* id_start;
const uint16_t* id_end;
};
typedef struct StarExportBinding StarExportBinding;
void bail (uint32_t err);
bool parseCJS (uint16_t* source, uint32_t sourceLen, void (*addExport)(const uint16_t*, const uint16_t*), void (*addReexport)(const uint16_t*, const uint16_t*), void (*addUnsafeGetter)(const uint16_t*, const uint16_t*), void (*clearReexports)());
enum RequireType {
Import,
ExportAssign,
ExportStar
};
void tryBacktrackAddStarExportBinding (uint16_t* pos);
bool tryParseRequire (enum RequireType requireType);
void tryParseLiteralExports ();
bool readExportsOrModuleDotExports (uint16_t ch);
void tryParseModuleExportsDotAssign ();
void tryParseExportsDotAssign (bool assign);
void tryParseObjectDefineOrKeys (bool keys);
bool identifier (uint16_t ch);
void throwIfImportStatement ();
void throwIfExportStatement ();
void readImportString (const uint16_t* ss, uint16_t ch);
uint16_t readExportAs (uint16_t* startPos, uint16_t* endPos);
uint16_t commentWhitespace ();
void stringLiteral (uint16_t quote);
void regularExpression ();
void templateString ();
void blockComment ();
void lineComment ();
uint16_t readToWsOrPunctuator (uint16_t ch);
uint32_t fullCharCodeAtLast (uint16_t* pos);
bool isIdentifierStart (uint32_t code);
bool isIdentifierChar (uint32_t code);
int charCodeByteLen (uint32_t ch);
bool isBr (uint16_t c);
bool isBrOrWs (uint16_t c);
bool isBrOrWsOrPunctuator (uint16_t c);
bool isBrOrWsOrPunctuatorNotDot (uint16_t c);
bool str_eq2 (uint16_t* pos, uint16_t c1, uint16_t c2);
bool str_eq3 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3);
bool str_eq4 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4);
bool str_eq5 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5);
bool str_eq6 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6);
bool str_eq7 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6, uint16_t c7);
bool str_eq9 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6, uint16_t c7, uint16_t c8, uint16_t c9);
bool str_eq10 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6, uint16_t c7, uint16_t c8, uint16_t c9, uint16_t c10);
bool str_eq13 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6, uint16_t c7, uint16_t c8, uint16_t c9, uint16_t c10, uint16_t c11, uint16_t c12, uint16_t c13);
bool str_eq18 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6, uint16_t c7, uint16_t c8, uint16_t c9, uint16_t c10, uint16_t c11, uint16_t c12, uint16_t c13, uint16_t c14, uint16_t c15, uint16_t c16, uint16_t c17, uint16_t c18);
bool str_eq22 (uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6, uint16_t c7, uint16_t c8, uint16_t c9, uint16_t c10, uint16_t c11, uint16_t c12, uint16_t c13, uint16_t c14, uint16_t c15, uint16_t c16, uint16_t c17, uint16_t c18, uint16_t c19, uint16_t c20, uint16_t c21, uint16_t c22);
bool readPrecedingKeyword2(uint16_t* pos, uint16_t c1, uint16_t c2);
bool readPrecedingKeyword3(uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3);
bool readPrecedingKeyword4(uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4);
bool readPrecedingKeyword5(uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5);
bool readPrecedingKeyword6(uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6);
bool readPrecedingKeyword7(uint16_t* pos, uint16_t c1, uint16_t c2, uint16_t c3, uint16_t c4, uint16_t c5, uint16_t c6, uint16_t c7);
bool keywordStart (uint16_t* pos);
bool isExpressionKeyword (uint16_t* pos);
bool isParenKeyword (uint16_t* pos);
bool isPunctuator (uint16_t charCode);
bool isExpressionPunctuator (uint16_t charCode);
bool isExpressionTerminator (uint16_t* pos);
void nextChar (uint16_t ch);
void nextCharSurrogate (uint16_t ch);
uint16_t readChar ();
void syntaxError ();
#ifdef __cplusplus
}
#endif
#endif /* __CJS_MODULE_LEXER_H__ */

7
cjs-module-lexer/lexer.d.ts vendored Executable file
View File

@ -0,0 +1,7 @@
export interface Exports {
exports: string[];
reexports: string[];
}
export declare function parse(source: string, name?: string): Exports;
export declare function init(): Promise<void>;

1438
cjs-module-lexer/lexer.js Executable file

File diff suppressed because it is too large Load Diff

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