Import Upstream version 27.5.1~ds+~cs69.51.22
This commit is contained in:
commit
c0606860b8
|
@ -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
|
|
@ -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
|
|
@ -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'),
|
||||
};
|
|
@ -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: {},
|
||||
},
|
||||
},
|
||||
};
|
|
@ -0,0 +1,3 @@
|
|||
|
||||
# Pings orta when PRs are to this module
|
||||
packages/jest-editor-support/src/* @orta
|
|
@ -0,0 +1 @@
|
|||
open_collective: jest
|
|
@ -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.
|
|
@ -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: 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
|
|
@ -0,0 +1 @@
|
|||
blank_issues_enabled: false
|
|
@ -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)
|
|
@ -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: This issue will be closed. :bangbang:
|
||||
options:
|
||||
- label: I understand
|
||||
required: true
|
|
@ -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. -->
|
|
@ -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 -->
|
|
@ -0,0 +1,6 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: daily
|
|
@ -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.
|
|
@ -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.
|
||||
|
|
@ -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 }}
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
[]
|
|
@ -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"
|
|
@ -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
|
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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.
|
|
@ -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.
|
|
@ -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> </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).
|
|
@ -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
|
|
@ -0,0 +1 @@
|
|||
* text=auto eol=lf
|
|
@ -0,0 +1,2 @@
|
|||
node_modules
|
||||
yarn.lock
|
|
@ -0,0 +1 @@
|
|||
package-lock=false
|
|
@ -0,0 +1,4 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- '10'
|
||||
- '8'
|
|
@ -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;
|
|
@ -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;
|
|
@ -0,0 +1,5 @@
|
|||
import {expectType} from 'tsd';
|
||||
import astralRegex = require('.');
|
||||
|
||||
expectType<RegExp>(astralRegex());
|
||||
expectType<RegExp>(astralRegex({exact: true}));
|
|
@ -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.
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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)
|
|
@ -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));
|
||||
}
|
||||
});
|
|
@ -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))
|
|
@ -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.
|
|
@ -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)
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
|
@ -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;
|
|
@ -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;
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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},
|
||||
},
|
||||
],
|
||||
],
|
||||
};
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
# Enforce `lf` for text files (even on Windows)
|
||||
text eol=lf
|
|
@ -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
|
|
@ -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.
|
|
@ -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.
|
|
@ -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)
|
|
@ -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);
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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,
|
||||
};
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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";
|
|
@ -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)!;
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
[1-9]*.test.js
|
|
@ -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', () => {});
|
|
@ -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
|
||||
```
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"jest": {
|
||||
"testEnvironment": "node"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env sh
|
||||
for i in {1..499}; do
|
||||
cp 0.test.js $i.test.js
|
||||
done
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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']
|
|
@ -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.
|
|
@ -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.
|
|
@ -0,0 +1,8 @@
|
|||
language: node_js
|
||||
|
||||
cache:
|
||||
- yarn
|
||||
|
||||
node_js:
|
||||
- "lts/*"
|
||||
- "node"
|
|
@ -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.
|
|
@ -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()
|
|
@ -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
|
|
@ -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")
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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])
|
||||
})
|
||||
})
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"plugins": [
|
||||
[
|
||||
"@babel/plugin-transform-modules-commonjs",
|
||||
{
|
||||
"strict": true
|
||||
},
|
||||
]
|
||||
]
|
||||
}
|
|
@ -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
|
|
@ -0,0 +1,3 @@
|
|||
node_modules
|
||||
dist
|
||||
yarn.lock
|
|
@ -0,0 +1,12 @@
|
|||
git:
|
||||
depth: 1
|
||||
language: node_js
|
||||
node_js:
|
||||
- '13'
|
||||
|
||||
before_install:
|
||||
- npm install
|
||||
script:
|
||||
- npm run build
|
||||
- npm run test
|
||||
|
|
@ -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)
|
|
@ -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.
|
|
@ -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/*
|
|
@ -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
|
|
@ -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')}`);
|
||||
}
|
||||
}
|
||||
});
|
|
@ -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);
|
|
@ -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 ();
|
|
@ -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__ */
|
|
@ -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>;
|
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
Loading…
Reference in New Issue