Deployed the page to Github Pages.

This commit is contained in:
Batuhan Berk Başoğlu 2024-11-03 21:30:09 -05:00
parent 1d79754e93
commit 2c89899458
Signed by: batuhan-basoglu
SSH key fingerprint: SHA256:kEsnuHX+qbwhxSAXPUQ4ox535wFHu/hIRaa53FzxRpo
62797 changed files with 6551425 additions and 15279 deletions

2579
node_modules/karma/CHANGELOG.md generated vendored Normal file

File diff suppressed because it is too large Load diff

13
node_modules/karma/CODE_OF_CONDUCT.md generated vendored Normal file
View file

@ -0,0 +1,13 @@
# Contributor Code of Conduct
As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion.
Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
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. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org/), version 1.0.0, available at <https://www.contributor-covenant.org/version/1/0/0/code-of-conduct/>

14
node_modules/karma/ISSUE_TEMPLATE.md generated vendored Normal file
View file

@ -0,0 +1,14 @@
### Expected behaviour
### Actual behaviour
### Environment Details
- Karma version (output of `karma --version`):
- Relevant part of your `karma.config.js` file
### Steps to reproduce the behaviour
1.
2.
3.

20
node_modules/karma/LICENSE generated vendored Normal file
View file

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

138
node_modules/karma/README.md generated vendored Normal file
View file

@ -0,0 +1,138 @@
# Karma
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/karma-runner/karma) [![npm version](https://img.shields.io/npm/v/karma.svg?style=flat-square)](https://www.npmjs.com/package/karma) [![npm downloads](https://img.shields.io/npm/dm/karma.svg?style=flat-square)](https://npmcharts.com/compare/karma?minimal=true)
[![Code Climate](https://img.shields.io/codeclimate/maintainability/karma-runner/karma)](https://codeclimate.com/github/karma-runner/karma) [![PRs Welcome](https://img.shields.io/badge/prs-welcome-brightgreen.svg?style=flat-square)](https://makeapullrequest.com/) [![Dependency Status](https://img.shields.io/david/karma-runner/karma.svg?style=flat-square)](https://david-dm.org/karma-runner/karma) [![devDependency Status](https://img.shields.io/david/dev/karma-runner/karma.svg?style=flat-square)](https://david-dm.org/karma-runner/karma#info=devDependencies) [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release)
A simple tool that allows you to execute JavaScript code in multiple
_real_ browsers.
> The main purpose of Karma is to make your test-driven development easy,
> fast, and fun.
## Karma is deprecated and is not accepting new features or general bug fixes.
The web testing space has evolved significantly in the [10+ years](https://testing.googleblog.com/2012/11/testacular-spectacular-test-runner-for.html) since Karma's creation. The web landscape looks very different today and new patterns and tools have emerged in the ecosystem. New test runners offer more performant alternatives, and Karma no longer provides clear unique value.
Based on the current state of the web testing ecosystem, we have made the hard decision to deprecate Karma.
We know Karma is used particularly commonly in the Angular ecosystem, so Angular is adding [Jest](https://jestjs.io/) and [Web Test Runner](https://modern-web.dev/docs/test-runner/overview/) support to provide a migration path off of Karma. See the [Angular blog](https://blog.angular.io/moving-angular-cli-to-jest-and-web-test-runner-ef85ef69ceca) for more details.
Critical security issues in Karma will still be triaged and fixed as necessary. This will continue until 12 months after Angular CLI's Web Test Runner support is marked stable.
For those outside Angular looking to migrate off Karma, both [Web Test Runner](https://modern-web.dev/docs/test-runner/overview/) and [`jasmine-browser-runner`](https://github.com/jasmine/jasmine-browser-runner) provide browser-based unit testing solutions which can be used as a direct alternative. [Jest](https://jestjs.io/) and [Vitest](https://vitest.dev/) also provide Node-based alternatives.
It has been incredible to see Karma's impact on the web testing ecosystem and we greatly appreciate the support of everyone who contributed to an awesome community. Keep testing. ✅
## Help and Support
> For questions and support please use the mailing list or Gitter.
> The issue tracker is for bug reports and feature discussions only.
* Obligatory [documentation]
* Quick questions:
[![Gitter Chat](https://img.shields.io/badge/GITTER-join%20chat-green.svg?style=flat-square)](https://gitter.im/karma-runner/karma)
* Longer questions: [Mailing List]
* Bug reports [Issue Tracker]
* Everything less than 140 characters: [@JsKarma] on Twitter
## When should I use Karma?
* You want to test code in *real* browsers.
* You want to test code in multiple browsers (desktop, mobile,
tablets, etc.).
* You want to execute your tests locally during development.
* You want to execute your tests on a continuous integration server.
* You want to execute your tests on every save.
* You love your terminal.
* You don't want your (testing) life to suck.
* You want to use [Istanbul] to automagically generate coverage
reports.
* You want to use [RequireJS] for your source files.
## But I still want to use \_insert testing library\_
Karma is not a testing framework, nor an assertion library.
Karma just launches an HTTP server, and generates the test runner HTML file you probably already know from your favourite testing framework.
So for testing purposes you can use pretty much anything you like. There are already plugins for most of the common testing frameworks:
* [Jasmine]
* [Mocha]
* [QUnit]
* and [many others](https://www.npmjs.com/search?q=keywords:karma-adapter)
If you can't find an adapter for your favourite framework, don't worry and write your own.
It's not that hard and we are here to help.
## Which Browsers can I use?
All the major browsers are supported, if you want to know more see the
[browsers] page.
## Troubleshooting
See [FAQ](https://karma-runner.github.io/latest/intro/faq.html).
## I want to use it. Where do I sign?
You don't need to sign anything but here are some resources to help
you to get started...
### Obligatory Screencast.
Every serious project has a screencast, so here is ours. Just click
[here] and let the show begin.
### Installation.
See [installation](https://karma-runner.github.io/latest/intro/installation.html).
### Using it.
See [configuration](https://karma-runner.github.io/latest/intro/configuration.html).
## This is so great. I want to help.
Please, see
[contributing](https://karma-runner.github.io/latest/dev/contributing.html).
## Why did you create this?
Throughout the development of [AngularJS], we've been using [JSTD] for
testing. I really think that JSTD is a great idea. Unfortunately, we
had many problems with JSTD, so we decided to write our own test
runner based on the same idea. We wanted a simple tool just for
executing JavaScript tests that is both stable and fast. That's why we
use the awesome [Socket.io] library and [Node.js].
## My boss wants a license. So where is it?
[MIT License](./LICENSE)
[AngularJS]: https://angularjs.org/
[JSTD]: https://code.google.com/p/js-test-driver/
[Socket.io]: https://socket.io/
[Node.js]: https://nodejs.org/
[Jasmine]: https://github.com/karma-runner/karma-jasmine
[Mocha]: https://github.com/karma-runner/karma-mocha
[QUnit]: https://github.com/karma-runner/karma-qunit
[here]: https://www.youtube.com/watch?v=MVw8N3hTfCI
[Mailing List]: https://groups.google.com/forum/#!forum/karma-users
[Issue Tracker]: https://github.com/karma-runner/karma/issues
[@JsKarma]: https://twitter.com/JsKarma
[RequireJS]: https://requirejs.org/
[Istanbul]: https://github.com/gotwarlost/istanbul
[browsers]: https://karma-runner.github.io/latest/config/browsers.html
[documentation]: https://karma-runner.github.io

11
node_modules/karma/SECURITY.md generated vendored Normal file
View file

@ -0,0 +1,11 @@
# Security Policy
## Supported Versions
Only the latest version of the project are currently being supported with security updates.
## Reporting a Vulnerability
To report a security issue, please email karma-runner-eng+security@google.com
with a description of the issue, the steps you took to create the issue,
affected versions, and if known, mitigations for the issue.

3
node_modules/karma/bin/karma generated vendored Executable file
View file

@ -0,0 +1,3 @@
#!/usr/bin/env node
require('../lib/cli').run();

1
node_modules/karma/commitlint.config.js generated vendored Normal file
View file

@ -0,0 +1 @@
module.exports = { extends: ['@commitlint/config-angular'] }

100
node_modules/karma/common/stringify.js generated vendored Normal file
View file

@ -0,0 +1,100 @@
var serialize = null
try {
serialize = require('dom-serialize')
} catch (e) {
// Ignore failure on IE8
}
var instanceOf = require('./util').instanceOf
function isNode (obj) {
return (obj.tagName || obj.nodeName) && obj.nodeType
}
function stringify (obj, depth) {
if (depth === 0) {
return '...'
}
if (obj === null) {
return 'null'
}
switch (typeof obj) {
case 'symbol':
return obj.toString()
case 'string':
return "'" + obj + "'"
case 'undefined':
return 'undefined'
case 'function':
try {
// function abc(a, b, c) { /* code goes here */ }
// -> function abc(a, b, c) { ... }
return obj.toString().replace(/\{[\s\S]*\}/, '{ ... }')
} catch (err) {
if (err instanceof TypeError) {
// Support older browsers
return 'function ' + (obj.name || '') + '() { ... }'
} else {
throw err
}
}
case 'boolean':
return obj ? 'true' : 'false'
case 'object':
var strs = []
if (instanceOf(obj, 'Array')) {
strs.push('[')
for (var i = 0, ii = obj.length; i < ii; i++) {
if (i) {
strs.push(', ')
}
strs.push(stringify(obj[i], depth - 1))
}
strs.push(']')
} else if (instanceOf(obj, 'Date')) {
return obj.toString()
} else if (instanceOf(obj, 'Text')) {
return obj.nodeValue
} else if (instanceOf(obj, 'Comment')) {
return '<!--' + obj.nodeValue + '-->'
} else if (obj.outerHTML) {
return obj.outerHTML
} else if (isNode(obj)) {
if (serialize) {
return serialize(obj)
} else {
return 'Skipping stringify, no support for dom-serialize'
}
} else if (instanceOf(obj, 'Error')) {
return obj.toString() + '\n' + obj.stack
} else {
var constructor = 'Object'
if (obj.constructor && typeof obj.constructor === 'function') {
constructor = obj.constructor.name
}
strs.push(constructor)
strs.push('{')
var first = true
for (var key in obj) {
if (Object.prototype.hasOwnProperty.call(obj, key)) {
if (first) {
first = false
} else {
strs.push(', ')
}
strs.push(key + ': ' + stringify(obj[key], depth - 1))
}
}
strs.push('}')
}
return strs.join('')
default:
return obj
}
}
module.exports = stringify

32
node_modules/karma/common/util.js generated vendored Normal file
View file

@ -0,0 +1,32 @@
exports.instanceOf = function (value, constructorName) {
return Object.prototype.toString.apply(value) === '[object ' + constructorName + ']'
}
exports.elm = function (id) {
return document.getElementById(id)
}
exports.generateId = function (prefix) {
return prefix + Math.floor(Math.random() * 10000)
}
exports.isUndefined = function (value) {
return typeof value === 'undefined'
}
exports.isDefined = function (value) {
return !exports.isUndefined(value)
}
exports.parseQueryParams = function (locationSearch) {
var params = {}
var pairs = locationSearch.slice(1).split('&')
var keyValue
for (var i = 0; i < pairs.length; i++) {
keyValue = pairs[i].split('=')
params[decodeURIComponent(keyValue[0])] = decodeURIComponent(keyValue[1])
}
return params
}

71
node_modules/karma/config.tpl.coffee generated vendored Normal file
View file

@ -0,0 +1,71 @@
# Karma configuration
# Generated on %DATE%
module.exports = (config) ->
config.set
# base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '%BASE_PATH%'
# frameworks to use
# available frameworks: https://www.npmjs.com/search?q=keywords:karma-adapter
frameworks: [%FRAMEWORKS%]
# list of files / patterns to load in the browser
files: [%FILES%
]
# list of files / patterns to exclude
exclude: [%EXCLUDE%
]
# preprocess matching files before serving them to the browser
# available preprocessors: https://www.npmjs.com/search?q=keywords:karma-preprocessor
preprocessors: {%PREPROCESSORS%
}
# test results reporter to use
# possible values: 'dots', 'progress'
# available reporters: https://www.npmjs.com/search?q=keywords:karma-reporter
reporters: ['progress']
# web server port
port: 9876
# enable / disable colors in the output (reporters and logs)
colors: true
# level of logging
# possible values:
# - config.LOG_DISABLE
# - config.LOG_ERROR
# - config.LOG_WARN
# - config.LOG_INFO
# - config.LOG_DEBUG
logLevel: config.LOG_INFO
# enable / disable watching file and executing tests whenever any file changes
autoWatch: %AUTO_WATCH%
# start these browsers
# available browser launchers: https://www.npmjs.com/search?q=keywords:karma-launcher
browsers: [%BROWSERS%]
# Continuous Integration mode
# if true, Karma captures browsers, runs the tests and exits
singleRun: false
# Concurrency level
# how many browser instances should be started simultaneously
concurrency: Infinity

68
node_modules/karma/config.tpl.js generated vendored Normal file
View file

@ -0,0 +1,68 @@
// Karma configuration
// Generated on %DATE%
module.exports = function(config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '%BASE_PATH%',
// frameworks to use
// available frameworks: https://www.npmjs.com/search?q=keywords:karma-adapter
frameworks: [%FRAMEWORKS%],
// list of files / patterns to load in the browser
files: [%FILES%
],
// list of files / patterns to exclude
exclude: [%EXCLUDE%
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://www.npmjs.com/search?q=keywords:karma-preprocessor
preprocessors: {%PREPROCESSORS%
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://www.npmjs.com/search?q=keywords:karma-reporter
reporters: ['progress'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: %AUTO_WATCH%,
// start these browsers
// available browser launchers: https://www.npmjs.com/search?q=keywords:karma-launcher
browsers: [%BROWSERS%],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false,
// Concurrency level
// how many browser instances should be started simultaneously
concurrency: Infinity
})
}

70
node_modules/karma/config.tpl.ls generated vendored Normal file
View file

@ -0,0 +1,70 @@
# Karma configuration
# Generated on %DATE%
module.exports = (config) ->
config.set do
# base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '%BASE_PATH%'
# frameworks to use
# available frameworks: https://www.npmjs.com/search?q=keywords:karma-adapter
frameworks: [%FRAMEWORKS%]
# list of files / patterns to load in the browser
files: [%FILES%
]
# list of files / patterns to exclude
exclude: [%EXCLUDE%
]
# preprocess matching files before serving them to the browser
# available preprocessors: https://www.npmjs.com/search?q=keywords:karma-preprocessor
preprocessors: {%PREPROCESSORS%
}
# test results reporter to use
# possible values: 'dots', 'progress'
# available reporters: https://www.npmjs.com/search?q=keywords:karma-reporter
reporters: ['progress']
# web server port
port: 9876
# enable / disable colors in the output (reporters and logs)
colors: true
# level of logging
# possible values:
# - config.LOG_DISABLE
# - config.LOG_ERROR
# - config.LOG_WARN
# - config.LOG_INFO
# - config.LOG_DEBUG
logLevel: config.LOG_INFO
# enable / disable watching file and executing tests whenever any file changes
autoWatch: %AUTO_WATCH%
# satart these browsers
# available browser launchers: https://www.npmjs.com/search?q=keywords:karma-launcher
browsers: [%BROWSERS%]
# Continuous Integration mode
# if true, Karma captures browsers, runs the tests and exits
singleRun: false
# Concurrency level
# how many browser instances should be started simultaneously
concurrency: Infinity

68
node_modules/karma/config.tpl.ts generated vendored Normal file
View file

@ -0,0 +1,68 @@
// Karma configuration
// Generated on %DATE%
module.exports = (config) => {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '%BASE_PATH%',
// frameworks to use
// available frameworks: https://www.npmjs.com/search?q=keywords:karma-adapter
frameworks: [%FRAMEWORKS%],
// list of files / patterns to load in the browser
files: [%FILES%
],
// list of files / patterns to exclude
exclude: [%EXCLUDE%
],
// preprocess matching files before serving them to the browser
// available preprocessors: https://www.npmjs.com/search?q=keywords:karma-preprocessor
preprocessors: {%PREPROCESSORS%
},
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://www.npmjs.com/search?q=keywords:karma-reporter
reporters: ['progress'],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: %AUTO_WATCH%,
// start these browsers
// available browser launchers: https://www.npmjs.com/search?q=keywords:karma-launcher
browsers: [%BROWSERS%],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: false,
// Concurrency level
// how many browser instances should be started simultaneously
concurrency: Infinity
})
}

161
node_modules/karma/context/karma.js generated vendored Normal file
View file

@ -0,0 +1,161 @@
// Load our dependencies
var stringify = require('../common/stringify')
// Define our context Karma constructor
function ContextKarma (callParentKarmaMethod) {
// Define local variables
var hasError = false
var self = this
var isLoaded = false
// Define our loggers
// DEV: These are intentionally repeated in client and context
this.log = function (type, args) {
var values = []
for (var i = 0; i < args.length; i++) {
values.push(this.stringify(args[i], 3))
}
this.info({ log: values.join(', '), type: type })
}
this.stringify = stringify
// Define our proxy error handler
// DEV: We require one in our context to track `hasError`
this.error = function () {
hasError = true
callParentKarmaMethod('error', [].slice.call(arguments))
return false
}
// Define our start handler
function UNIMPLEMENTED_START () {
this.error('You need to include some adapter that implements __karma__.start method!')
}
// all files loaded, let's start the execution
this.loaded = function () {
// has error -> cancel
if (!hasError && !isLoaded) {
isLoaded = true
try {
this.start(this.config)
} catch (error) {
this.error(error.stack || error.toString())
}
}
// remove reference to child iframe
this.start = UNIMPLEMENTED_START
}
// supposed to be overridden by the context
// TODO(vojta): support multiple callbacks (queue)
this.start = UNIMPLEMENTED_START
// Define proxy methods
// DEV: This is a closured `for` loop (same as a `forEach`) for IE support
var proxyMethods = ['complete', 'info', 'result']
for (var i = 0; i < proxyMethods.length; i++) {
(function bindProxyMethod (methodName) {
self[methodName] = function boundProxyMethod () {
callParentKarmaMethod(methodName, [].slice.call(arguments))
}
}(proxyMethods[i]))
}
// Define bindings for context window
this.setupContext = function (contextWindow) {
// If we clear the context after every run and we already had an error
// then stop now. Otherwise, carry on.
if (self.config.clearContext && hasError) {
return
}
// Perform window level bindings
// DEV: We return `self.error` since we want to `return false` to ignore errors
contextWindow.onerror = function () {
return self.error.apply(self, arguments)
}
contextWindow.onbeforeunload = function () {
return self.error('Some of your tests did a full page reload!')
}
contextWindow.dump = function () {
self.log('dump', arguments)
}
var _confirm = contextWindow.confirm
var _prompt = contextWindow.prompt
contextWindow.alert = function (msg) {
self.log('alert', [msg])
}
contextWindow.confirm = function (msg) {
self.log('confirm', [msg])
return _confirm(msg)
}
contextWindow.prompt = function (msg, defaultVal) {
self.log('prompt', [msg, defaultVal])
return _prompt(msg, defaultVal)
}
// If we want to overload our console, then do it
function getConsole (currentWindow) {
return currentWindow.console || {
log: function () {},
info: function () {},
warn: function () {},
error: function () {},
debug: function () {}
}
}
if (self.config.captureConsole) {
// patch the console
var localConsole = contextWindow.console = getConsole(contextWindow)
var logMethods = ['log', 'info', 'warn', 'error', 'debug']
var patchConsoleMethod = function (method) {
var orig = localConsole[method]
if (!orig) {
return
}
localConsole[method] = function () {
self.log(method, arguments)
try {
return Function.prototype.apply.call(orig, localConsole, arguments)
} catch (error) {
self.log('warn', ['Console method ' + method + ' threw: ' + error])
}
}
}
for (var i = 0; i < logMethods.length; i++) {
patchConsoleMethod(logMethods[i])
}
}
}
}
// Define call/proxy methods
ContextKarma.getDirectCallParentKarmaMethod = function (parentWindow) {
return function directCallParentKarmaMethod (method, args) {
// If the method doesn't exist, then error out
if (!parentWindow.karma[method]) {
parentWindow.karma.error('Expected Karma method "' + method + '" to exist but it doesn\'t')
return
}
// Otherwise, run our method
parentWindow.karma[method].apply(parentWindow.karma, args)
}
}
ContextKarma.getPostMessageCallParentKarmaMethod = function (parentWindow) {
return function postMessageCallParentKarmaMethod (method, args) {
parentWindow.postMessage({ __karmaMethod: method, __karmaArguments: args }, window.location.origin)
}
}
// Export our module
module.exports = ContextKarma

21
node_modules/karma/context/main.js generated vendored Normal file
View file

@ -0,0 +1,21 @@
// Load in our dependencies
var ContextKarma = require('./karma')
// Resolve our parent window
var parentWindow = window.opener || window.parent
// Define a remote call method for Karma
var callParentKarmaMethod = ContextKarma.getDirectCallParentKarmaMethod(parentWindow)
// If we don't have access to the window, then use `postMessage`
// DEV: In Electron, we don't have access to the parent window due to it being in a separate process
// DEV: We avoid using this in Internet Explorer as they only support strings
// https://caniuse.com/?search=postmessage
var haveParentAccess = false
try { haveParentAccess = !!parentWindow.window } catch (err) { /* Ignore errors (likely permission errors) */ }
if (!haveParentAccess) {
callParentKarmaMethod = ContextKarma.getPostMessageCallParentKarmaMethod(parentWindow)
}
// Define a window-scoped Karma
window.__karma__ = new ContextKarma(callParentKarmaMethod)

13
node_modules/karma/cucumber.js generated vendored Normal file
View file

@ -0,0 +1,13 @@
// Shared configuration for Cucumber.js tests.
// See https://github.com/cucumber/cucumber-js/blob/master/docs/cli.md#profiles
const options = [
'--format progress',
'--require test/e2e/support/env.js',
'--require test/e2e/support/world.js',
'--require test/e2e/step_definitions/core_steps.js',
'--require test/e2e/step_definitions/hooks.js'
]
module.exports = {
default: options.join(' ')
}

271
node_modules/karma/lib/browser.js generated vendored Normal file
View file

@ -0,0 +1,271 @@
'use strict'
const BrowserResult = require('./browser_result')
const helper = require('./helper')
const logger = require('./logger')
const CONNECTED = 'CONNECTED' // The browser is connected but not yet been commanded to execute tests.
const CONFIGURING = 'CONFIGURING' // The browser has been told to execute tests; it is configuring before tests execution.
const EXECUTING = 'EXECUTING' // The browser is executing the tests.
const EXECUTING_DISCONNECTED = 'EXECUTING_DISCONNECTED' // The browser is executing the tests, but temporarily disconnect (waiting for socket reconnecting).
const DISCONNECTED = 'DISCONNECTED' // The browser got completely disconnected (e.g. browser crash) and can be only restored with a restart of execution.
class Browser {
constructor (id, fullName, collection, emitter, socket, timer, disconnectDelay,
noActivityTimeout, singleRun, clientConfig) {
this.id = id
this.fullName = fullName
this.name = helper.browserFullNameToShort(fullName)
this.lastResult = new BrowserResult()
this.disconnectsCount = 0
this.activeSockets = [socket]
this.noActivityTimeout = noActivityTimeout
this.singleRun = singleRun
this.clientConfig = clientConfig
this.collection = collection
this.emitter = emitter
this.socket = socket
this.timer = timer
this.disconnectDelay = disconnectDelay
this.log = logger.create(this.name)
this.noActivityTimeoutId = null
this.pendingDisconnect = null
this.setState(CONNECTED)
}
init () {
this.log.info(`Connected on socket ${this.socket.id} with id ${this.id}`)
this.bindSocketEvents(this.socket)
this.collection.add(this)
this.emitter.emit('browser_register', this)
}
setState (toState) {
this.log.debug(`${this.state} -> ${toState}`)
this.state = toState
}
onKarmaError (error) {
if (this.isNotConnected()) {
this.lastResult.error = true
}
this.emitter.emit('browser_error', this, error)
this.refreshNoActivityTimeout()
}
onInfo (info) {
if (helper.isDefined(info.dump)) {
this.emitter.emit('browser_log', this, info.dump, 'dump')
}
if (helper.isDefined(info.log)) {
this.emitter.emit('browser_log', this, info.log, info.type)
} else if (helper.isDefined(info.total)) {
if (this.state === EXECUTING) {
this.lastResult.total = info.total
}
} else if (!helper.isDefined(info.dump)) {
this.emitter.emit('browser_info', this, info)
}
this.refreshNoActivityTimeout()
}
onStart (info) {
if (info.total === null) {
this.log.warn('Adapter did not report total number of specs.')
}
this.lastResult = new BrowserResult(info.total)
this.setState(EXECUTING)
this.emitter.emit('browser_start', this, info)
this.refreshNoActivityTimeout()
}
onComplete (result) {
if (this.isNotConnected()) {
this.setState(CONNECTED)
this.lastResult.totalTimeEnd()
this.emitter.emit('browsers_change', this.collection)
this.emitter.emit('browser_complete', this, result)
this.clearNoActivityTimeout()
}
}
onSocketDisconnect (reason, disconnectedSocket) {
helper.arrayRemove(this.activeSockets, disconnectedSocket)
if (this.activeSockets.length) {
this.log.debug(`Disconnected ${disconnectedSocket.id}, still have ${this.getActiveSocketsIds()}`)
return
}
if (this.isConnected()) {
this.disconnect(`Client disconnected from CONNECTED state (${reason})`)
} else if ([CONFIGURING, EXECUTING].includes(this.state)) {
this.log.debug(`Disconnected during run, waiting ${this.disconnectDelay}ms for reconnecting.`)
this.setState(EXECUTING_DISCONNECTED)
this.pendingDisconnect = this.timer.setTimeout(() => {
this.lastResult.totalTimeEnd()
this.lastResult.disconnected = true
this.disconnect(`reconnect failed before timeout of ${this.disconnectDelay}ms (${reason})`)
this.emitter.emit('browser_complete', this)
}, this.disconnectDelay)
this.clearNoActivityTimeout()
}
}
reconnect (newSocket, clientSaysReconnect) {
if (!clientSaysReconnect || this.state === DISCONNECTED) {
this.log.info(`Disconnected browser returned on socket ${newSocket.id} with id ${this.id}.`)
this.setState(CONNECTED)
// The disconnected browser is already part of the collection.
// Update the collection view in the UI (header on client.html)
this.emitter.emit('browsers_change', this.collection)
// Notify the launcher
this.emitter.emit('browser_register', this)
// Execute tests if configured to do so.
if (this.singleRun) {
this.execute()
}
} else if (this.state === EXECUTING_DISCONNECTED) {
this.log.debug('Lost socket connection, but browser continued to execute. Reconnected ' +
`on socket ${newSocket.id}.`)
this.setState(EXECUTING)
} else if ([CONNECTED, CONFIGURING, EXECUTING].includes(this.state)) {
this.log.debug(`Rebinding to new socket ${newSocket.id} (already have ` +
`${this.getActiveSocketsIds()})`)
}
if (!this.activeSockets.some((s) => s.id === newSocket.id)) {
this.activeSockets.push(newSocket)
this.bindSocketEvents(newSocket)
}
if (this.pendingDisconnect) {
this.timer.clearTimeout(this.pendingDisconnect)
}
this.refreshNoActivityTimeout()
}
onResult (result) {
if (Array.isArray(result)) {
result.forEach(this.onResult, this)
} else if (this.isNotConnected()) {
this.lastResult.add(result)
this.emitter.emit('spec_complete', this, result)
}
this.refreshNoActivityTimeout()
}
execute () {
this.activeSockets.forEach((socket) => socket.emit('execute', this.clientConfig))
this.setState(CONFIGURING)
this.refreshNoActivityTimeout()
}
getActiveSocketsIds () {
return this.activeSockets.map((s) => s.id).join(', ')
}
disconnect (reason) {
this.log.warn(`Disconnected (${this.disconnectsCount} times) ${reason || ''}`)
this.disconnectsCount++
this.emitter.emit('browser_error', this, `Disconnected ${reason || ''}`)
this.remove()
}
remove () {
this.setState(DISCONNECTED)
this.collection.remove(this)
}
refreshNoActivityTimeout () {
if (this.noActivityTimeout) {
this.clearNoActivityTimeout()
this.noActivityTimeoutId = this.timer.setTimeout(() => {
this.lastResult.totalTimeEnd()
this.lastResult.disconnected = true
this.disconnect(`, because no message in ${this.noActivityTimeout} ms.`)
this.emitter.emit('browser_complete', this)
}, this.noActivityTimeout)
}
}
clearNoActivityTimeout () {
if (this.noActivityTimeout && this.noActivityTimeoutId) {
this.timer.clearTimeout(this.noActivityTimeoutId)
this.noActivityTimeoutId = null
}
}
bindSocketEvents (socket) {
// TODO: check which of these events are actually emitted by socket
socket.on('disconnect', (reason) => this.onSocketDisconnect(reason, socket))
socket.on('start', (info) => this.onStart(info))
socket.on('karma_error', (error) => this.onKarmaError(error))
socket.on('complete', (result) => this.onComplete(result))
socket.on('info', (info) => this.onInfo(info))
socket.on('result', (result) => this.onResult(result))
}
isConnected () {
return this.state === CONNECTED
}
isNotConnected () {
return !this.isConnected()
}
serialize () {
return {
id: this.id,
name: this.name,
isConnected: this.state === CONNECTED
}
}
toString () {
return this.name
}
toJSON () {
return {
id: this.id,
fullName: this.fullName,
name: this.name,
state: this.state,
lastResult: this.lastResult,
disconnectsCount: this.disconnectsCount,
noActivityTimeout: this.noActivityTimeout,
disconnectDelay: this.disconnectDelay
}
}
}
Browser.factory = function (
id, fullName, /* capturedBrowsers */ collection, emitter, socket, timer,
/* config.browserDisconnectTimeout */ disconnectDelay,
/* config.browserNoActivityTimeout */ noActivityTimeout,
/* config.singleRun */ singleRun,
/* config.client */ clientConfig) {
return new Browser(id, fullName, collection, emitter, socket, timer,
disconnectDelay, noActivityTimeout, singleRun, clientConfig)
}
Browser.STATE_CONNECTED = CONNECTED
Browser.STATE_CONFIGURING = CONFIGURING
Browser.STATE_EXECUTING = EXECUTING
Browser.STATE_EXECUTING_DISCONNECTED = EXECUTING_DISCONNECTED
Browser.STATE_DISCONNECTED = DISCONNECTED
module.exports = Browser

103
node_modules/karma/lib/browser_collection.js generated vendored Normal file
View file

@ -0,0 +1,103 @@
'use strict'
const BrowserResult = require('./browser_result')
const helper = require('./helper')
class BrowserCollection {
constructor (emitter, browsers = []) {
this.browsers = browsers
this.emitter = emitter
}
add (browser) {
this.browsers.push(browser)
this.emitter.emit('browsers_change', this)
}
remove (browser) {
if (helper.arrayRemove(this.browsers, browser)) {
this.emitter.emit('browsers_change', this)
return true
}
return false
}
getById (browserId) {
return this.browsers.find((browser) => browser.id === browserId) || null
}
getNonReady () {
return this.browsers.filter((browser) => !browser.isConnected())
}
areAllReady () {
return this.browsers.every((browser) => browser.isConnected())
}
serialize () {
return this.browsers.map((browser) => browser.serialize())
}
calculateExitCode (results, singleRunBrowserNotCaptured, config) {
config = config || {}
if (results.disconnected || singleRunBrowserNotCaptured) {
return 1
}
if (results.skipped && config.failOnSkippedTests) {
return 1
}
if (results.success + results.failed === 0 && !!config.failOnEmptyTestSuite) {
return 1
}
if (results.error) {
return 1
}
if (config.failOnFailingTestSuite === false) {
return 0 // Tests executed without infrastructure error, exit with 0 independent of test status.
}
return results.failed ? 1 : 0
}
getResults (singleRunBrowserNotCaptured, config) {
const results = { success: 0, failed: 0, skipped: 0, error: false, disconnected: false, exitCode: 0 }
this.browsers.forEach((browser) => {
results.success += browser.lastResult.success
results.failed += browser.lastResult.failed
results.skipped += browser.lastResult.skipped
results.error = results.error || browser.lastResult.error
results.disconnected = results.disconnected || browser.lastResult.disconnected
})
results.exitCode = this.calculateExitCode(results, singleRunBrowserNotCaptured, config)
return results
}
clearResults () {
this.browsers.forEach((browser) => {
browser.lastResult = new BrowserResult()
})
}
clone () {
return new BrowserCollection(this.emitter, this.browsers.slice())
}
// Array APIs
map (callback, context) {
return this.browsers.map(callback, context)
}
forEach (callback, context) {
return this.browsers.forEach(callback, context)
}
get length () {
return this.browsers.length
}
}
BrowserCollection.factory = function (emitter) {
return new BrowserCollection(emitter)
}
module.exports = BrowserCollection

30
node_modules/karma/lib/browser_result.js generated vendored Normal file
View file

@ -0,0 +1,30 @@
'use strict'
class BrowserResult {
constructor (total = 0) {
this.startTime = Date.now()
this.total = total
this.skipped = this.failed = this.success = 0
this.netTime = this.totalTime = 0
this.disconnected = this.error = false
}
totalTimeEnd () {
this.totalTime = Date.now() - this.startTime
}
add (result) {
if (result.skipped) {
this.skipped++
} else if (result.success) {
this.success++
} else {
this.failed++
}
this.netTime += result.time
}
}
module.exports = BrowserResult

331
node_modules/karma/lib/cli.js generated vendored Normal file
View file

@ -0,0 +1,331 @@
'use strict'
const path = require('path')
const yargs = require('yargs')
const fs = require('graceful-fs')
const Server = require('./server')
const helper = require('./helper')
const constant = require('./constants')
const cfg = require('./config')
function processArgs (argv, options, fs, path) {
Object.getOwnPropertyNames(argv).forEach(function (name) {
let argumentValue = argv[name]
if (name !== '_' && name !== '$0') {
if (Array.isArray(argumentValue)) {
argumentValue = argumentValue.pop() // If the same argument is defined multiple times, override.
}
options[helper.dashToCamel(name)] = argumentValue
}
})
if (helper.isString(options.autoWatch)) {
options.autoWatch = options.autoWatch === 'true'
}
if (helper.isString(options.colors)) {
options.colors = options.colors === 'true'
}
if (helper.isString(options.failOnEmptyTestSuite)) {
options.failOnEmptyTestSuite = options.failOnEmptyTestSuite === 'true'
}
if (helper.isString(options.failOnFailingTestSuite)) {
options.failOnFailingTestSuite = options.failOnFailingTestSuite === 'true'
}
if (helper.isString(options.formatError)) {
let required
try {
required = require(options.formatError)
} catch (err) {
console.error('Could not require formatError: ' + options.formatError, err)
}
// support exports.formatError and module.exports = function
options.formatError = required.formatError || required
if (!helper.isFunction(options.formatError)) {
console.error(`Format error must be a function, got: ${typeof options.formatError}`)
process.exit(1)
}
}
if (helper.isString(options.logLevel)) {
const logConstant = constant['LOG_' + options.logLevel.toUpperCase()]
if (helper.isDefined(logConstant)) {
options.logLevel = logConstant
} else {
console.error('Log level must be one of disable, error, warn, info, or debug.')
process.exit(1)
}
} else if (helper.isDefined(options.logLevel)) {
console.error('Log level must be one of disable, error, warn, info, or debug.')
process.exit(1)
}
if (helper.isString(options.singleRun)) {
options.singleRun = options.singleRun === 'true'
}
if (helper.isString(options.browsers)) {
options.browsers = options.browsers.split(',')
}
if (options.reportSlowerThan === false) {
options.reportSlowerThan = 0
}
if (helper.isString(options.reporters)) {
options.reporters = options.reporters.split(',')
}
if (helper.isString(options.removedFiles)) {
options.removedFiles = options.removedFiles.split(',')
}
if (helper.isString(options.addedFiles)) {
options.addedFiles = options.addedFiles.split(',')
}
if (helper.isString(options.changedFiles)) {
options.changedFiles = options.changedFiles.split(',')
}
if (helper.isString(options.refresh)) {
options.refresh = options.refresh === 'true'
}
let configFile = argv.configFile
if (!configFile) {
// default config file (if exists)
if (fs.existsSync('./karma.conf.js')) {
configFile = './karma.conf.js'
} else if (fs.existsSync('./karma.conf.coffee')) {
configFile = './karma.conf.coffee'
} else if (fs.existsSync('./karma.conf.ts')) {
configFile = './karma.conf.ts'
} else if (fs.existsSync('./.config/karma.conf.js')) {
configFile = './.config/karma.conf.js'
} else if (fs.existsSync('./.config/karma.conf.coffee')) {
configFile = './.config/karma.conf.coffee'
} else if (fs.existsSync('./.config/karma.conf.ts')) {
configFile = './.config/karma.conf.ts'
}
}
options.configFile = configFile ? path.resolve(configFile) : null
if (options.cmd === 'run') {
options.clientArgs = parseClientArgs(process.argv)
}
return options
}
function parseClientArgs (argv) {
// extract any args after '--' as clientArgs
let clientArgs = []
argv = argv.slice(2)
const idx = argv.indexOf('--')
if (idx !== -1) {
clientArgs = argv.slice(idx + 1)
}
return clientArgs
}
// return only args that occur before `--`
function argsBeforeDoubleDash (argv) {
const idx = argv.indexOf('--')
return idx === -1 ? argv : argv.slice(0, idx)
}
function describeRoot () {
return yargs
.usage('Karma - Spectacular Test Runner for JavaScript.\n\n' +
'Run --help with particular command to see its description and available options.\n\n' +
'Usage:\n' +
' $0 <command>')
.command('init [configFile]', 'Initialize a config file.', describeInit)
.command('start [configFile]', 'Start the server / do a single run.', describeStart)
.command('run [configFile]', 'Trigger a test run.', describeRun)
.command('stop [configFile]', 'Stop the server.', describeStop)
.command('completion', 'Shell completion for karma.', describeCompletion)
.demandCommand(1, 'Command not specified.')
.strictCommands()
.describe('help', 'Print usage and options.')
.describe('version', 'Print current version.')
}
function describeInit (yargs) {
yargs
.usage('Karma - Spectacular Test Runner for JavaScript.\n\n' +
'INIT - Initialize a config file.\n\n' +
'Usage:\n' +
' $0 init [configFile]')
.strictCommands(false)
.version(false)
.positional('configFile', {
describe: 'Name of the generated Karma configuration file',
type: 'string'
})
.describe('log-level', '<disable | error | warn | info | debug> Level of logging.')
.describe('colors', 'Use colors when reporting and printing logs.')
.describe('no-colors', 'Do not use colors when reporting or printing logs.')
}
function describeStart (yargs) {
yargs
.usage('Karma - Spectacular Test Runner for JavaScript.\n\n' +
'START - Start the server / do a single run.\n\n' +
'Usage:\n' +
' $0 start [configFile]')
.strictCommands(false)
.version(false)
.positional('configFile', {
describe: 'Path to the Karma configuration file',
type: 'string'
})
.describe('port', '<integer> Port where the server is running.')
.describe('auto-watch', 'Auto watch source files and run on change.')
.describe('detached', 'Detach the server.')
.describe('no-auto-watch', 'Do not watch source files.')
.describe('log-level', '<disable | error | warn | info | debug> Level of logging.')
.describe('colors', 'Use colors when reporting and printing logs.')
.describe('no-colors', 'Do not use colors when reporting or printing logs.')
.describe('reporters', 'List of reporters (available: dots, progress, junit, growl, coverage).')
.describe('browsers', 'List of browsers to start (eg. --browsers Chrome,ChromeCanary,Firefox).')
.describe('capture-timeout', '<integer> Kill browser if does not capture in given time [ms].')
.describe('single-run', 'Run the test when browsers captured and exit.')
.describe('no-single-run', 'Disable single-run.')
.describe('report-slower-than', '<integer> Report tests that are slower than given time [ms].')
.describe('fail-on-empty-test-suite', 'Fail on empty test suite.')
.describe('no-fail-on-empty-test-suite', 'Do not fail on empty test suite.')
.describe('fail-on-failing-test-suite', 'Fail on failing test suite.')
.describe('no-fail-on-failing-test-suite', 'Do not fail on failing test suite.')
.option('format-error', {
describe: 'A path to a file that exports the format function.',
type: 'string'
})
}
function describeRun (yargs) {
yargs
.usage('Karma - Spectacular Test Runner for JavaScript.\n\n' +
'RUN - Run the tests (requires running server).\n\n' +
'Usage:\n' +
' $0 run [configFile] [-- <clientArgs>]')
.strictCommands(false)
.version(false)
.positional('configFile', {
describe: 'Path to the Karma configuration file',
type: 'string'
})
.describe('port', '<integer> Port where the server is listening.')
.describe('no-refresh', 'Do not re-glob all the patterns.')
.describe('fail-on-empty-test-suite', 'Fail on empty test suite.')
.describe('no-fail-on-empty-test-suite', 'Do not fail on empty test suite.')
.describe('log-level', '<disable | error | warn | info | debug> Level of logging.')
.describe('colors', 'Use colors when reporting and printing logs.')
.describe('no-colors', 'Do not use colors when reporting or printing logs.')
.option('removed-files', {
describe: 'Comma-separated paths to removed files. Useful when automatic file watching is disabled.',
type: 'string'
})
.option('changed-files', {
describe: 'Comma-separated paths to changed files. Useful when automatic file watching is disabled.',
type: 'string'
})
.option('added-files', {
describe: 'Comma-separated paths to added files. Useful when automatic file watching is disabled.',
type: 'string'
})
}
function describeStop (yargs) {
yargs
.usage('Karma - Spectacular Test Runner for JavaScript.\n\n' +
'STOP - Stop the server (requires running server).\n\n' +
'Usage:\n' +
' $0 stop [configFile]')
.strictCommands(false)
.version(false)
.positional('configFile', {
describe: 'Path to the Karma configuration file',
type: 'string'
})
.describe('port', '<integer> Port where the server is listening.')
.describe('log-level', '<disable | error | warn | info | debug> Level of logging.')
}
function describeCompletion (yargs) {
yargs
.usage('Karma - Spectacular Test Runner for JavaScript.\n\n' +
'COMPLETION - Bash/ZSH completion for karma.\n\n' +
'Installation:\n' +
' $0 completion >> ~/.bashrc')
.version(false)
}
function printRunnerProgress (data) {
process.stdout.write(data)
}
exports.process = () => {
const argv = describeRoot().parse(argsBeforeDoubleDash(process.argv.slice(2)))
return processArgs(argv, { cmd: argv._.shift() }, fs, path)
}
exports.run = async () => {
const cliOptions = exports.process()
const cmd = cliOptions.cmd // prevent config from changing the command
const cmdNeedsConfig = cmd === 'start' || cmd === 'run' || cmd === 'stop'
if (cmdNeedsConfig) {
let config
try {
config = await cfg.parseConfig(
cliOptions.configFile,
cliOptions,
{
promiseConfig: true,
throwErrors: true
}
)
} catch (karmaConfigException) {
// The reject reason/exception isn't used to log a message since
// parseConfig already calls a configured logger method with an almost
// identical message.
// The `run` function is a private application, not a public API. We don't
// need to worry about process.exit vs throw vs promise rejection here.
process.exit(1)
}
switch (cmd) {
case 'start': {
const server = new Server(config)
await server.start()
return server
}
case 'run':
return require('./runner')
.run(config)
.on('progress', printRunnerProgress)
case 'stop':
return require('./stopper').stop(config)
}
} else {
switch (cmd) {
case 'init':
return require('./init').init(cliOptions)
case 'completion':
return require('./completion').completion(cliOptions)
}
}
}
// just for testing
exports.processArgs = processArgs
exports.parseClientArgs = parseClientArgs
exports.argsBeforeDoubleDash = argsBeforeDoubleDash

126
node_modules/karma/lib/completion.js generated vendored Normal file
View file

@ -0,0 +1,126 @@
'use strict'
const glob = require('glob')
const CUSTOM = ['']
const BOOLEAN = false
const options = {
start: {
'--port': CUSTOM,
'--auto-watch': BOOLEAN,
'--no-auto-watch': BOOLEAN,
'--log-level': ['disable', 'debug', 'info', 'warn', 'error'],
'--colors': BOOLEAN,
'--no-colors': BOOLEAN,
'--reporters': ['dots', 'progress'],
'--no-reporters': BOOLEAN,
'--browsers': ['Chrome', 'ChromeHeadless', 'ChromeCanary', 'Firefox', 'PhantomJS', 'Safari', 'Opera'],
'--no-browsers': BOOLEAN,
'--single-run': BOOLEAN,
'--no-single-run': BOOLEAN,
'--help': BOOLEAN
},
init: {
'--log-level': ['disable', 'debug', 'info', 'warn', 'error'],
'--colors': BOOLEAN,
'--no-colors': BOOLEAN,
'--help': BOOLEAN
},
run: {
'--no-refresh': BOOLEAN,
'--port': CUSTOM,
'--help': BOOLEAN
}
}
function opositeWord (word) {
if (word.startsWith('-')) {
return word.startsWith('--no-') ? `--${word.slice(5)}` : `--no-${word.slice(2)}`
} else {
return null
}
}
function sendCompletion (possibleWords, env) {
const regexp = new RegExp('^' + env.last)
possibleWords
.filter((word) => regexp.test(word) && !env.words.includes(word) && !env.words.includes(opositeWord(word)))
.forEach((word) => {
console.log(word)
})
}
function sendCompletionFiles (env) {
glob(env.last + '*', { mark: true, nocase: true }, (err, files) => {
if (err) return console.error(err)
if (files.length === 1 && files[0].endsWith('/')) {
sendCompletionFiles({ last: files[0] })
} else {
console.log(files.join('\n'))
}
})
}
function complete (env) {
if (env.count === 1) {
return sendCompletion(env.words[0].startsWith('-') ? ['--help', '--version'] : Object.keys(options), env)
} else if (env.count === 2 && !env.words[1].startsWith('-')) {
return sendCompletionFiles(env)
}
const cmdOptions = options[env.words[0]]
if (cmdOptions) {
if (cmdOptions[env.prev] === CUSTOM && env.last) {
console.log(env.last)
} else {
return sendCompletion(cmdOptions[env.prev] || Object.keys(cmdOptions), env)
}
}
}
function completion () {
if (process.argv[3] === '--') {
return complete({
words: process.argv.slice(5),
count: parseInt(process.env.COMP_CWORD, 10),
last: process.argv[process.argv.length - 1],
prev: process.argv[process.argv.length - 2]
})
}
// just print out the karma-completion.sh
const fs = require('graceful-fs')
const path = require('path')
fs.readFile(path.resolve(__dirname, '../scripts/karma-completion.sh'), 'utf8', function (err, data) {
if (err) return console.error(err)
process.stdout.write(data)
process.stdout.on('error', function (error) {
// Darwin is a real dick sometimes.
//
// This is necessary because the "source" or "." program in
// bash on OS X closes its file argument before reading
// from it, meaning that you get exactly 1 write, which will
// work most of the time, and will always raise an EPIPE.
//
// Really, one should not be tossing away EPIPE errors, or any
// errors, so casually. But, without this, `. <(karma completion)`
// can never ever work on OS X.
if (error.errno === 'EPIPE') {
error = null
}
})
})
}
// PUBLIC API
exports.completion = completion
// for testing
exports.opositeWord = opositeWord
exports.sendCompletion = sendCompletion
exports.complete = complete

544
node_modules/karma/lib/config.js generated vendored Normal file
View file

@ -0,0 +1,544 @@
'use strict'
const path = require('path')
const assert = require('assert')
const logger = require('./logger')
const log = logger.create('config')
const helper = require('./helper')
const constant = require('./constants')
const _ = require('lodash')
let COFFEE_SCRIPT_AVAILABLE = false
let LIVE_SCRIPT_AVAILABLE = false
let TYPE_SCRIPT_AVAILABLE = false
try {
require('coffeescript').register()
COFFEE_SCRIPT_AVAILABLE = true
} catch {}
// LiveScript is required here to enable config files written in LiveScript.
// It's not directly used in this file.
try {
require('LiveScript')
LIVE_SCRIPT_AVAILABLE = true
} catch {}
try {
require('ts-node')
TYPE_SCRIPT_AVAILABLE = true
} catch {}
class Pattern {
constructor (pattern, served, included, watched, nocache, type, isBinary, integrity) {
this.pattern = pattern
this.served = helper.isDefined(served) ? served : true
this.included = helper.isDefined(included) ? included : true
this.watched = helper.isDefined(watched) ? watched : true
this.nocache = helper.isDefined(nocache) ? nocache : false
this.weight = helper.mmPatternWeight(pattern)
this.type = type
this.isBinary = isBinary
this.integrity = integrity
}
compare (other) {
return helper.mmComparePatternWeights(this.weight, other.weight)
}
}
class UrlPattern extends Pattern {
constructor (url, type, integrity) {
super(url, false, true, false, false, type, undefined, integrity)
}
}
function createPatternObject (pattern) {
if (pattern && helper.isString(pattern)) {
return helper.isUrlAbsolute(pattern)
? new UrlPattern(pattern)
: new Pattern(pattern)
} else if (helper.isObject(pattern) && pattern.pattern && helper.isString(pattern.pattern)) {
return helper.isUrlAbsolute(pattern.pattern)
? new UrlPattern(pattern.pattern, pattern.type, pattern.integrity)
: new Pattern(pattern.pattern, pattern.served, pattern.included, pattern.watched, pattern.nocache, pattern.type)
} else {
log.warn(`Invalid pattern ${pattern}!\n\tExpected string or object with "pattern" property.`)
return new Pattern(null, false, false, false, false)
}
}
function normalizeUrl (url) {
if (!url.startsWith('/')) {
url = `/${url}`
}
if (!url.endsWith('/')) {
url = url + '/'
}
return url
}
function normalizeUrlRoot (urlRoot) {
const normalizedUrlRoot = normalizeUrl(urlRoot)
if (normalizedUrlRoot !== urlRoot) {
log.warn(`urlRoot normalized to "${normalizedUrlRoot}"`)
}
return normalizedUrlRoot
}
function normalizeProxyPath (proxyPath) {
const normalizedProxyPath = normalizeUrl(proxyPath)
if (normalizedProxyPath !== proxyPath) {
log.warn(`proxyPath normalized to "${normalizedProxyPath}"`)
}
return normalizedProxyPath
}
function normalizeConfig (config, configFilePath) {
function basePathResolve (relativePath) {
if (helper.isUrlAbsolute(relativePath)) {
return relativePath
} else if (helper.isDefined(config.basePath) && helper.isDefined(relativePath)) {
return path.resolve(config.basePath, relativePath)
} else {
return ''
}
}
function createPatternMapper (resolve) {
return (objectPattern) => Object.assign(objectPattern, { pattern: resolve(objectPattern.pattern) })
}
if (helper.isString(configFilePath)) {
config.basePath = path.resolve(path.dirname(configFilePath), config.basePath) // resolve basePath
config.exclude.push(configFilePath) // always ignore the config file itself
} else {
config.basePath = path.resolve(config.basePath || '.')
}
config.files = config.files.map(createPatternObject).map(createPatternMapper(basePathResolve))
config.exclude = config.exclude.map(basePathResolve)
config.customContextFile = config.customContextFile && basePathResolve(config.customContextFile)
config.customDebugFile = config.customDebugFile && basePathResolve(config.customDebugFile)
config.customClientContextFile = config.customClientContextFile && basePathResolve(config.customClientContextFile)
// normalize paths on windows
config.basePath = helper.normalizeWinPath(config.basePath)
config.files = config.files.map(createPatternMapper(helper.normalizeWinPath))
config.exclude = config.exclude.map(helper.normalizeWinPath)
config.customContextFile = helper.normalizeWinPath(config.customContextFile)
config.customDebugFile = helper.normalizeWinPath(config.customDebugFile)
config.customClientContextFile = helper.normalizeWinPath(config.customClientContextFile)
// normalize urlRoot
config.urlRoot = normalizeUrlRoot(config.urlRoot)
// normalize and default upstream proxy settings if given
if (config.upstreamProxy) {
const proxy = config.upstreamProxy
proxy.path = helper.isDefined(proxy.path) ? normalizeProxyPath(proxy.path) : '/'
proxy.hostname = helper.isDefined(proxy.hostname) ? proxy.hostname : 'localhost'
proxy.port = helper.isDefined(proxy.port) ? proxy.port : 9875
// force protocol to end with ':'
proxy.protocol = (proxy.protocol || 'http').split(':')[0] + ':'
if (proxy.protocol.match(/https?:/) === null) {
log.warn(`"${proxy.protocol}" is not a supported upstream proxy protocol, defaulting to "http:"`)
proxy.protocol = 'http:'
}
}
// force protocol to end with ':'
config.protocol = (config.protocol || 'http').split(':')[0] + ':'
if (config.protocol.match(/https?:/) === null) {
log.warn(`"${config.protocol}" is not a supported protocol, defaulting to "http:"`)
config.protocol = 'http:'
}
if (config.proxies && Object.prototype.hasOwnProperty.call(config.proxies, config.urlRoot)) {
log.warn(`"${config.urlRoot}" is proxied, you should probably change urlRoot to avoid conflicts`)
}
if (config.singleRun && config.autoWatch) {
log.debug('autoWatch set to false, because of singleRun')
config.autoWatch = false
}
if (config.runInParent) {
log.debug('useIframe set to false, because using runInParent')
config.useIframe = false
}
if (!config.singleRun && !config.useIframe && config.runInParent) {
log.debug('singleRun set to true, because using runInParent')
config.singleRun = true
}
if (helper.isString(config.reporters)) {
config.reporters = config.reporters.split(',')
}
if (config.client && config.client.args) {
assert(Array.isArray(config.client.args), 'Invalid configuration: client.args must be an array of strings')
}
if (config.browsers) {
assert(Array.isArray(config.browsers), 'Invalid configuration: browsers option must be an array')
}
if (config.formatError) {
assert(helper.isFunction(config.formatError), 'Invalid configuration: formatError option must be a function.')
}
if (config.processKillTimeout) {
assert(helper.isNumber(config.processKillTimeout), 'Invalid configuration: processKillTimeout option must be a number.')
}
if (config.browserSocketTimeout) {
assert(helper.isNumber(config.browserSocketTimeout), 'Invalid configuration: browserSocketTimeout option must be a number.')
}
if (config.pingTimeout) {
assert(helper.isNumber(config.pingTimeout), 'Invalid configuration: pingTimeout option must be a number.')
}
const defaultClient = config.defaultClient || {}
Object.keys(defaultClient).forEach(function (key) {
const option = config.client[key]
config.client[key] = helper.isDefined(option) ? option : defaultClient[key]
})
// normalize preprocessors
const preprocessors = config.preprocessors || {}
const normalizedPreprocessors = config.preprocessors = Object.create(null)
Object.keys(preprocessors).forEach(function (pattern) {
const normalizedPattern = helper.normalizeWinPath(basePathResolve(pattern))
normalizedPreprocessors[normalizedPattern] = helper.isString(preprocessors[pattern])
? [preprocessors[pattern]] : preprocessors[pattern]
})
// define custom launchers/preprocessors/reporters - create a new plugin
const module = Object.create(null)
let hasSomeInlinedPlugin = false
const types = ['launcher', 'preprocessor', 'reporter']
types.forEach(function (type) {
const definitions = config[`custom${helper.ucFirst(type)}s`] || {}
Object.keys(definitions).forEach(function (name) {
const definition = definitions[name]
if (!helper.isObject(definition)) {
return log.warn(`Can not define ${type} ${name}. Definition has to be an object.`)
}
if (!helper.isString(definition.base)) {
return log.warn(`Can not define ${type} ${name}. Missing base ${type}.`)
}
const token = type + ':' + definition.base
const locals = {
args: ['value', definition]
}
module[type + ':' + name] = ['factory', function (injector) {
const plugin = injector.createChild([locals], [token]).get(token)
if (type === 'launcher' && helper.isDefined(definition.displayName)) {
plugin.displayName = definition.displayName
}
return plugin
}]
hasSomeInlinedPlugin = true
})
})
if (hasSomeInlinedPlugin) {
config.plugins.push(module)
}
return config
}
/**
* @class
*/
class Config {
constructor () {
this.LOG_DISABLE = constant.LOG_DISABLE
this.LOG_ERROR = constant.LOG_ERROR
this.LOG_WARN = constant.LOG_WARN
this.LOG_INFO = constant.LOG_INFO
this.LOG_DEBUG = constant.LOG_DEBUG
// DEFAULT CONFIG
this.frameworks = []
this.protocol = 'http:'
this.port = constant.DEFAULT_PORT
this.listenAddress = constant.DEFAULT_LISTEN_ADDR
this.hostname = constant.DEFAULT_HOSTNAME
this.httpsServerConfig = {}
this.basePath = ''
this.files = []
this.browserConsoleLogOptions = {
level: 'debug',
format: '%b %T: %m',
terminal: true
}
this.customContextFile = null
this.customDebugFile = null
this.customClientContextFile = null
this.exclude = []
this.logLevel = constant.LOG_INFO
this.colors = true
this.autoWatch = true
this.autoWatchBatchDelay = 250
this.restartOnFileChange = false
this.usePolling = process.platform === 'linux'
this.reporters = ['progress']
this.singleRun = false
this.browsers = []
this.captureTimeout = 60000
this.pingTimeout = 5000
this.proxies = {}
this.proxyValidateSSL = true
this.preprocessors = {}
this.preprocessor_priority = {}
this.urlRoot = '/'
this.upstreamProxy = undefined
this.reportSlowerThan = 0
this.loggers = [constant.CONSOLE_APPENDER]
this.transports = ['polling', 'websocket']
this.forceJSONP = false
this.plugins = ['karma-*']
this.defaultClient = this.client = {
args: [],
useIframe: true,
runInParent: false,
captureConsole: true,
clearContext: true,
allowedReturnUrlPatterns: ['^https?://']
}
this.browserDisconnectTimeout = 2000
this.browserDisconnectTolerance = 0
this.browserNoActivityTimeout = 30000
this.processKillTimeout = 2000
this.concurrency = Infinity
this.failOnEmptyTestSuite = true
this.retryLimit = 2
this.detached = false
this.crossOriginAttribute = true
this.browserSocketTimeout = 20000
}
set (newConfig) {
_.mergeWith(this, newConfig, (obj, src) => {
// Overwrite arrays to keep consistent with #283
if (Array.isArray(src)) {
return src
}
})
}
}
const CONFIG_SYNTAX_HELP = ' module.exports = function(config) {\n' +
' config.set({\n' +
' // your config\n' +
' });\n' +
' };\n'
/**
* Retrieve a parsed and finalized Karma `Config` instance. This `karmaConfig`
* object may be used to configure public API methods such a `Server`,
* `runner.run`, and `stopper.stop`.
*
* @param {?string} [configFilePath=null]
* A string representing a file system path pointing to the config file
* whose default export is a function that will be used to set Karma
* configuration options. This function will be passed an instance of the
* `Config` class as its first argument. If this option is not provided,
* then only the options provided by the `cliOptions` argument will be
* set.
* @param {Object} cliOptions
* An object whose values will take priority over options set in the
* config file. The config object passed to function exported by the
* config file will already have these options applied. Any changes the
* config file makes to these options will effectively be ignored in the
* final configuration.
*
* `cliOptions` all the same options as the config file and is applied
* using the same `config.set()` method.
* @param {Object} parseOptions
* @param {boolean} [parseOptions.promiseConfig=false]
* When `true`, a promise that resolves to a `Config` object will be
* returned. This also allows the function exported by config files (if
* provided) to be asynchronous by returning a promise. Resolving this
* promise indicates that all async activity has completed. The resolution
* value itself is ignored, all configuration must be done with
* `config.set`.
* @param {boolean} [parseOptions.throwErrors=false]
* When `true`, process exiting on critical failures will be disabled. In
* The error will be thrown as an exception. If
* `parseOptions.promiseConfig` is also `true`, then the error will
* instead be used as the promise's reject reason.
* @returns {Config|Promise<Config>}
*/
function parseConfig (configFilePath, cliOptions, parseOptions) {
const promiseConfig = parseOptions && parseOptions.promiseConfig === true
const throwErrors = parseOptions && parseOptions.throwErrors === true
const shouldSetupLoggerEarly = promiseConfig
if (shouldSetupLoggerEarly) {
// `setupFromConfig` provides defaults for `colors` and `logLevel`.
// `setup` provides defaults for `appenders`
// The first argument MUST BE an object
logger.setupFromConfig({})
}
function fail () {
log.error(...arguments)
if (throwErrors) {
const errorMessage = Array.from(arguments).join(' ')
const err = new Error(errorMessage)
if (promiseConfig) {
return Promise.reject(err)
}
throw err
} else {
const warningMessage =
'The `parseConfig()` function historically called `process.exit(1)`' +
' when it failed. This behavior is now deprecated and function will' +
' throw an error in the next major release. To suppress this warning' +
' pass `throwErrors: true` as a third argument to opt-in into the new' +
' behavior and adjust your code to respond to the exception' +
' accordingly.' +
' Example: `parseConfig(path, cliOptions, { throwErrors: true })`'
log.warn(warningMessage)
process.exit(1)
}
}
let configModule
if (configFilePath) {
try {
if (path.extname(configFilePath) === '.ts' && TYPE_SCRIPT_AVAILABLE) {
require('ts-node').register()
}
configModule = require(configFilePath)
if (typeof configModule === 'object' && typeof configModule.default !== 'undefined') {
configModule = configModule.default
}
} catch (e) {
const extension = path.extname(configFilePath)
if (extension === '.coffee' && !COFFEE_SCRIPT_AVAILABLE) {
log.error('You need to install CoffeeScript.\n npm install coffeescript --save-dev')
} else if (extension === '.ls' && !LIVE_SCRIPT_AVAILABLE) {
log.error('You need to install LiveScript.\n npm install LiveScript --save-dev')
} else if (extension === '.ts' && !TYPE_SCRIPT_AVAILABLE) {
log.error('You need to install TypeScript.\n npm install typescript ts-node --save-dev')
}
return fail('Error in config file!\n ' + e.stack || e)
}
if (!helper.isFunction(configModule)) {
return fail('Config file must export a function!\n' + CONFIG_SYNTAX_HELP)
}
} else {
configModule = () => {} // if no config file path is passed, we define a dummy config module.
}
const config = new Config()
// save and reset hostname and listenAddress so we can detect if the user
// changed them
const defaultHostname = config.hostname
config.hostname = null
const defaultListenAddress = config.listenAddress
config.listenAddress = null
// add the user's configuration in
config.set(cliOptions)
let configModuleReturn
try {
configModuleReturn = configModule(config)
} catch (e) {
return fail('Error in config file!\n', e)
}
function finalizeConfig (config) {
// merge the config from config file and cliOptions (precedence)
config.set(cliOptions)
// if the user changed listenAddress, but didn't set a hostname, warn them
if (config.hostname === null && config.listenAddress !== null) {
log.warn(`ListenAddress was set to ${config.listenAddress} but hostname was left as the default: ` +
`${defaultHostname}. If your browsers fail to connect, consider changing the hostname option.`)
}
// restore values that weren't overwritten by the user
if (config.hostname === null) {
config.hostname = defaultHostname
}
if (config.listenAddress === null) {
config.listenAddress = defaultListenAddress
}
// configure the logger as soon as we can
logger.setup(config.logLevel, config.colors, config.loggers)
log.debug(configFilePath ? `Loading config ${configFilePath}` : 'No config file specified.')
return normalizeConfig(config, configFilePath)
}
/**
* Return value is a function or (non-null) object that has a `then` method.
*
* @type {boolean}
* @see {@link https://promisesaplus.com/}
*/
const returnIsThenable = (
(
(configModuleReturn != null && typeof configModuleReturn === 'object') ||
typeof configModuleReturn === 'function'
) && typeof configModuleReturn.then === 'function'
)
if (returnIsThenable) {
if (promiseConfig !== true) {
const errorMessage =
'The `parseOptions.promiseConfig` option must be set to `true` to ' +
'enable promise return values from configuration files. ' +
'Example: `parseConfig(path, cliOptions, { promiseConfig: true })`'
return fail(errorMessage)
}
return configModuleReturn.then(
function onKarmaConfigModuleFulfilled (/* ignoredResolutionValue */) {
return finalizeConfig(config)
},
function onKarmaConfigModuleRejected (reason) {
return fail('Error in config file!\n', reason)
}
)
} else {
if (promiseConfig) {
try {
return Promise.resolve(finalizeConfig(config))
} catch (exception) {
return Promise.reject(exception)
}
} else {
return finalizeConfig(config)
}
}
}
// PUBLIC API
exports.parseConfig = parseConfig
exports.Pattern = Pattern
exports.createPatternObject = createPatternObject
exports.Config = Config

43
node_modules/karma/lib/constants.js generated vendored Normal file
View file

@ -0,0 +1,43 @@
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '/../package.json')).toString())
exports.VERSION = pkg.version
exports.DEFAULT_PORT = process.env.PORT || 9876
exports.DEFAULT_HOSTNAME = process.env.IP || 'localhost'
exports.DEFAULT_LISTEN_ADDR = process.env.LISTEN_ADDR || '0.0.0.0'
// log levels
exports.LOG_DISABLE = 'OFF'
exports.LOG_ERROR = 'ERROR'
exports.LOG_WARN = 'WARN'
exports.LOG_INFO = 'INFO'
exports.LOG_DEBUG = 'DEBUG'
exports.LOG_LOG = 'LOG'
exports.LOG_PRIORITIES = [
exports.LOG_DISABLE,
exports.LOG_ERROR,
exports.LOG_WARN,
exports.LOG_LOG,
exports.LOG_INFO,
exports.LOG_DEBUG
]
// Default patterns for the pattern layout.
exports.COLOR_PATTERN = '%[%d{DATETIME}:%p [%c]: %]%m'
exports.NO_COLOR_PATTERN = '%d{DATETIME}:%p [%c]: %m'
// Default console appender
exports.CONSOLE_APPENDER = {
type: 'console',
layout: {
type: 'pattern',
pattern: exports.COLOR_PATTERN
}
}
exports.EXIT_CODE = '\x1FEXIT'

11
node_modules/karma/lib/detached.js generated vendored Normal file
View file

@ -0,0 +1,11 @@
'use strict'
const fs = require('fs')
const Server = require('./server')
const configurationFile = process.argv[2]
const fileContents = fs.readFileSync(configurationFile, 'utf-8')
fs.unlink(configurationFile, function () {})
const data = JSON.parse(fileContents)
const server = new Server(data)
server.start(data)

33
node_modules/karma/lib/emitter_wrapper.js generated vendored Normal file
View file

@ -0,0 +1,33 @@
'use strict'
class EmitterWrapper {
constructor (emitter) {
this.listeners = {}
this.emitter = emitter
}
addListener (event, listener) {
this.emitter.addListener(event, listener)
this.listeners[event] = this.listeners[event] || []
this.listeners[event].push(listener)
return this
}
on (event, listener) {
return this.addListener(event, listener)
}
removeAllListeners (event) {
const events = event ? [event] : Object.keys(this.listeners)
events.forEach((event) => {
this.listeners[event].forEach((listener) => {
this.emitter.removeListener(event, listener)
})
delete this.listeners[event]
})
return this
}
}
module.exports = EmitterWrapper

67
node_modules/karma/lib/events.js generated vendored Normal file
View file

@ -0,0 +1,67 @@
'use strict'
const EventEmitter = require('events').EventEmitter
const helper = require('./helper')
function bufferEvents (emitter, eventsToBuffer) {
const listeners = []
const eventsToReply = []
function genericListener () {
eventsToReply.push(Array.from(arguments))
}
eventsToBuffer.forEach((eventName) => {
const listener = genericListener.bind(null, eventName)
listeners.push(listener)
emitter.on(eventName, listener)
})
return function () {
listeners.forEach((listener, i) => {
emitter.removeListener(eventsToBuffer[i], listener)
})
eventsToReply.forEach((args) => {
EventEmitter.prototype.emit.apply(emitter, args)
})
listeners.length = 0
eventsToReply.length = 0
}
}
class KarmaEventEmitter extends EventEmitter {
bind (object) {
for (const method in object) {
if (method.startsWith('on') && helper.isFunction(object[method])) {
this.on(helper.camelToSnake(method.slice(2)), function () {
// We do not use an arrow function here, to supply the caller as this.
object[method].apply(object, Array.from(arguments).concat(this))
})
}
}
}
emitAsync (name) {
// TODO(vojta): allow passing args
// TODO(vojta): ignore/throw if listener call done() multiple times
let pending = this.listeners(name).length
const deferred = helper.defer()
this.emit(name, () => {
if (!--pending) {
deferred.resolve()
}
})
if (!pending) {
deferred.resolve()
}
return deferred.promise
}
}
exports.EventEmitter = KarmaEventEmitter
exports.bufferEvents = bufferEvents

96
node_modules/karma/lib/executor.js generated vendored Normal file
View file

@ -0,0 +1,96 @@
'use strict'
const log = require('./logger').create()
class Executor {
constructor (capturedBrowsers, config, emitter) {
this.capturedBrowsers = capturedBrowsers
this.config = config
this.emitter = emitter
this.executionScheduled = false
this.errorsScheduled = []
this.pendingCount = 0
this.runningBrowsers = null
this.emitter.on('run_complete', () => this.onRunComplete())
this.emitter.on('browser_complete', () => this.onBrowserComplete())
}
schedule () {
if (this.capturedBrowsers.length === 0) {
log.warn(`No captured browser, open ${this.config.protocol}//${this.config.hostname}:${this.config.port}${this.config.urlRoot}`)
return false
} else if (this.capturedBrowsers.areAllReady()) {
log.debug('All browsers are ready, executing')
log.debug(`Captured ${this.capturedBrowsers.length} browsers`)
this.executionScheduled = false
this.capturedBrowsers.clearResults()
this.pendingCount = this.capturedBrowsers.length
this.runningBrowsers = this.capturedBrowsers.clone()
this.emitter.emit('run_start', this.runningBrowsers)
this.socketIoSockets.emit('execute', this.config.client)
return true
} else {
log.info('Delaying execution, these browsers are not ready: ' + this.capturedBrowsers.getNonReady().join(', '))
this.executionScheduled = true
return false
}
}
/**
* Schedule an error to be reported
* @param {string} errorMessage
* @returns {boolean} a boolean indicating whether or not the error was handled synchronously
*/
scheduleError (errorMessage) {
// We don't want to interfere with any running test.
// Verify that no test is running before reporting the error.
if (this.capturedBrowsers.areAllReady()) {
log.warn(errorMessage)
const errorResult = {
success: 0,
failed: 0,
skipped: 0,
error: errorMessage,
exitCode: 1
}
const noBrowsersStartedTests = []
this.emitter.emit('run_start', noBrowsersStartedTests) // A run cannot complete without being started
this.emitter.emit('run_complete', noBrowsersStartedTests, errorResult)
return true
} else {
this.errorsScheduled.push(errorMessage)
return false
}
}
onRunComplete () {
if (this.executionScheduled) {
this.schedule()
}
if (this.errorsScheduled.length) {
const errorsToReport = this.errorsScheduled
this.errorsScheduled = []
errorsToReport.forEach((error) => this.scheduleError(error))
}
}
onBrowserComplete () {
this.pendingCount--
if (!this.pendingCount) {
// Ensure run_complete is emitted in the next tick
// so it is never emitted before browser_complete
setTimeout(() => {
this.emitter.emit('run_complete', this.runningBrowsers, this.runningBrowsers.getResults())
})
}
}
}
Executor.factory = function (capturedBrowsers, config, emitter) {
return new Executor(capturedBrowsers, config, emitter)
}
module.exports = Executor

242
node_modules/karma/lib/file-list.js generated vendored Normal file
View file

@ -0,0 +1,242 @@
'use strict'
const { promisify } = require('util')
const mm = require('minimatch')
const Glob = require('glob').Glob
const fs = require('graceful-fs')
const statAsync = promisify(fs.stat.bind(fs))
const pathLib = require('path')
const _ = require('lodash')
const File = require('./file')
const Url = require('./url')
const helper = require('./helper')
const log = require('./logger').create('filelist')
const createPatternObject = require('./config').createPatternObject
class FileList {
constructor (patterns, excludes, emitter, preprocess, autoWatchBatchDelay) {
this._patterns = patterns || []
this._excludes = excludes || []
this._emitter = emitter
this._preprocess = preprocess
this.buckets = new Map()
// A promise that is pending if and only if we are active in this.refresh_()
this._refreshing = null
const emit = () => {
this._emitter.emit('file_list_modified', this.files)
}
const debouncedEmit = _.debounce(emit, autoWatchBatchDelay)
this._emitModified = (immediate) => {
immediate ? emit() : debouncedEmit()
}
}
_findExcluded (path) {
return this._excludes.find((pattern) => mm(path, pattern))
}
_findIncluded (path) {
return this._patterns.find((pattern) => mm(path, pattern.pattern))
}
_findFile (path, pattern) {
if (!path || !pattern) return
return this._getFilesByPattern(pattern.pattern).find((file) => file.originalPath === path)
}
_exists (path) {
return !!this._patterns.find((pattern) => mm(path, pattern.pattern) && this._findFile(path, pattern))
}
_getFilesByPattern (pattern) {
return this.buckets.get(pattern) || []
}
_refresh () {
const matchedFiles = new Set()
let lastCompletedRefresh = this._refreshing
lastCompletedRefresh = Promise.all(
this._patterns.map(async ({ pattern, type, nocache, isBinary, integrity }) => {
if (helper.isUrlAbsolute(pattern)) {
this.buckets.set(pattern, [new Url(pattern, type, integrity)])
return
}
const mg = new Glob(pathLib.normalize(pattern), { cwd: '/', follow: true, nodir: true, sync: true })
const files = mg.found
.filter((path) => {
if (this._findExcluded(path)) {
log.debug(`Excluded file "${path}"`)
return false
} else if (matchedFiles.has(path)) {
return false
} else {
matchedFiles.add(path)
return true
}
})
.map((path) => new File(path, mg.statCache[path].mtime, nocache, type, isBinary))
if (nocache) {
log.debug(`Not preprocessing "${pattern}" due to nocache`)
} else {
await Promise.all(files.map((file) => this._preprocess(file)))
}
this.buckets.set(pattern, files)
if (_.isEmpty(mg.found)) {
log.warn(`Pattern "${pattern}" does not match any file.`)
} else if (_.isEmpty(files)) {
log.warn(`All files matched by "${pattern}" were excluded or matched by prior matchers.`)
}
})
)
.then(() => {
// When we return from this function the file processing chain will be
// complete. In the case of two fast refresh() calls, the second call
// will overwrite this._refreshing, and we want the status to reflect
// the second call and skip the modification event from the first call.
if (this._refreshing !== lastCompletedRefresh) {
return this._refreshing
}
this._emitModified(true)
return this.files
})
return lastCompletedRefresh
}
get files () {
const served = []
const included = {}
const lookup = {}
this._patterns.forEach((p) => {
// This needs to be here sadly, as plugins are modifiying
// the _patterns directly resulting in elements not being
// instantiated properly
if (p.constructor.name !== 'Pattern') {
p = createPatternObject(p)
}
const files = this._getFilesByPattern(p.pattern)
files.sort((a, b) => {
if (a.path > b.path) return 1
if (a.path < b.path) return -1
return 0
})
if (p.served) {
served.push(...files)
}
files.forEach((file) => {
if (lookup[file.path] && lookup[file.path].compare(p) < 0) return
lookup[file.path] = p
if (p.included) {
included[file.path] = file
} else {
delete included[file.path]
}
})
})
return {
served: _.uniq(served, 'path'),
included: _.values(included)
}
}
refresh () {
this._refreshing = this._refresh()
return this._refreshing
}
reload (patterns, excludes) {
this._patterns = patterns || []
this._excludes = excludes || []
return this.refresh()
}
async addFile (path) {
const excluded = this._findExcluded(path)
if (excluded) {
log.debug(`Add file "${path}" ignored. Excluded by "${excluded}".`)
return this.files
}
const pattern = this._findIncluded(path)
if (!pattern) {
log.debug(`Add file "${path}" ignored. Does not match any pattern.`)
return this.files
}
if (this._exists(path)) {
log.debug(`Add file "${path}" ignored. Already in the list.`)
return this.files
}
const file = new File(path)
this._getFilesByPattern(pattern.pattern).push(file)
const [stat] = await Promise.all([statAsync(path), this._refreshing])
file.mtime = stat.mtime
await this._preprocess(file)
log.info(`Added file "${path}".`)
this._emitModified()
return this.files
}
async changeFile (path, force) {
const pattern = this._findIncluded(path)
const file = this._findFile(path, pattern)
if (!file) {
log.debug(`Changed file "${path}" ignored. Does not match any file in the list.`)
return this.files
}
const [stat] = await Promise.all([statAsync(path), this._refreshing])
if (force || stat.mtime > file.mtime) {
file.mtime = stat.mtime
await this._preprocess(file)
log.info(`Changed file "${path}".`)
this._emitModified(force)
}
return this.files
}
async removeFile (path) {
const pattern = this._findIncluded(path)
const file = this._findFile(path, pattern)
if (file) {
helper.arrayRemove(this._getFilesByPattern(pattern.pattern), file)
log.info(`Removed file "${path}".`)
this._emitModified()
} else {
log.debug(`Removed file "${path}" ignored. Does not match any file in the list.`)
}
return this.files
}
}
FileList.factory = function (config, emitter, preprocess) {
return new FileList(config.files, config.exclude, emitter, preprocess, config.autoWatchBatchDelay)
}
FileList.factory.$inject = ['config', 'emitter', 'preprocess']
module.exports = FileList

49
node_modules/karma/lib/file.js generated vendored Normal file
View file

@ -0,0 +1,49 @@
'use strict'
const path = require('path')
/**
* File object used for tracking files in `file-list.js`.
*/
class File {
constructor (path, mtime, doNotCache, type, isBinary, integrity) {
// used for serving (processed path, eg some/file.coffee -> some/file.coffee.js)
this.path = path
// original absolute path, id of the file
this.originalPath = path
// where the content is stored (processed)
this.contentPath = path
// encodings format {[encodingType]: encodedContent}
// example: {gzip: <Buffer 1f 8b 08...>}
this.encodings = Object.create(null)
this.mtime = mtime
this.isUrl = false
this.doNotCache = doNotCache === undefined ? false : doNotCache
this.type = type
// Tri state: null means probe file for binary.
this.isBinary = isBinary === undefined ? null : isBinary
this.integrity = integrity
}
/**
* Detect type from the file extension.
* @returns {string} detected file type or empty string
*/
detectType () {
return path.extname(this.path).slice(1)
}
toString () {
return this.path
}
}
module.exports = File

171
node_modules/karma/lib/helper.js generated vendored Normal file
View file

@ -0,0 +1,171 @@
'use strict'
const _ = require('lodash')
const mkdirp = require('mkdirp')
const useragent = require('ua-parser-js')
const mm = require('minimatch')
exports.browserFullNameToShort = (fullName) => {
const ua = useragent(fullName)
if (!ua.browser.name && !ua.browser.version && !ua.os.name && !ua.os.version) {
return fullName
}
return `${ua.browser.name} ${ua.browser.version || '0.0.0'} (${ua.os.name} ${ua.os.version || '0.0.0'})`
}
exports.isDefined = (value) => {
return !_.isUndefined(value)
}
const parser = (pattern, out) => {
if (pattern.length === 0) return out
const p = /^(\[[^\]]*\]|[*+@?]\((.+?)\))/g
const matches = p.exec(pattern)
if (!matches) {
const c = pattern[0]
let t = 'word'
if (c === '*') {
t = 'star'
} else if (c === '?') {
t = 'optional'
}
out[t]++
return parser(pattern.slice(1), out)
}
if (matches[2] !== undefined) {
out.ext_glob++
parser(matches[2], out)
return parser(pattern.slice(matches[0].length), out)
}
out.range++
return parser(pattern.slice(matches[0].length), out)
}
const gsParser = (pattern, out) => {
if (pattern === '**') {
out.glob_star++
return out
}
return parser(pattern, out)
}
const compareWeightObject = (w1, w2) => {
return exports.mmComparePatternWeights(
[w1.glob_star, w1.star, w1.ext_glob, w1.range, w1.optional],
[w2.glob_star, w2.star, w2.ext_glob, w2.range, w2.optional]
)
}
exports.mmPatternWeight = (pattern) => {
const m = new mm.Minimatch(pattern)
if (!m.globParts) return [0, 0, 0, 0, 0, 0]
const result = m.globParts.reduce((prev, p) => {
const r = p.reduce((prev, p) => {
return gsParser(p, prev)
}, { glob_star: 0, ext_glob: 0, word: 0, star: 0, optional: 0, range: 0 })
if (prev === undefined) return r
return compareWeightObject(r, prev) > 0 ? r : prev
}, undefined)
result.glob_sets = m.set.length
return [result.glob_sets, result.glob_star, result.star, result.ext_glob, result.range, result.optional]
}
exports.mmComparePatternWeights = (weight1, weight2) => {
const n1 = weight1[0]
const n2 = weight2[0]
const diff = n1 - n2
if (diff !== 0) return diff / Math.abs(diff)
return weight1.length > 1 ? exports.mmComparePatternWeights(weight1.slice(1), weight2.slice(1)) : 0
}
exports.isFunction = _.isFunction
exports.isString = _.isString
exports.isObject = _.isObject
exports.isArray = _.isArray
exports.isNumber = _.isNumber
const ABS_URL = /^https?:\/\//
exports.isUrlAbsolute = (url) => {
return ABS_URL.test(url)
}
exports.camelToSnake = (camelCase) => {
return camelCase.replace(/[A-Z]/g, (match, pos) => {
return (pos > 0 ? '_' : '') + match.toLowerCase()
})
}
exports.ucFirst = (word) => {
return word.charAt(0).toUpperCase() + word.slice(1)
}
exports.dashToCamel = (dash) => {
const words = dash.split('-')
return words.shift() + words.map(exports.ucFirst).join('')
}
exports.arrayRemove = (collection, item) => {
const idx = collection.indexOf(item)
if (idx !== -1) {
collection.splice(idx, 1)
return true
}
return false
}
exports.merge = function () {
const args = Array.prototype.slice.call(arguments, 0)
args.unshift({})
return _.merge.apply({}, args)
}
exports.formatTimeInterval = (time) => {
const mins = Math.floor(time / 60000)
const secs = (time - mins * 60000) / 1000
let str = secs + (secs === 1 ? ' sec' : ' secs')
if (mins) {
str = mins + (mins === 1 ? ' min ' : ' mins ') + str
}
return str
}
const replaceWinPath = (path) => {
return _.isString(path) ? path.replace(/\\/g, '/') : path
}
exports.normalizeWinPath = process.platform === 'win32' ? replaceWinPath : _.identity
exports.mkdirIfNotExists = (directory, done) => {
mkdirp(directory, done)
}
exports.defer = () => {
let res
let rej
const promise = new Promise((resolve, reject) => {
res = resolve
rej = reject
})
return {
resolve: res,
reject: rej,
promise: promise
}
}
exports.saveOriginalArgs = (config) => {
if (config && config.client && config.client.args) {
config.client.originalArgs = _.cloneDeep(config.client.args)
}
}
exports.restoreOriginalArgs = (config) => {
if (config && config.client && config.client.originalArgs) {
config.client.args = _.cloneDeep(config.client.originalArgs)
}
}

18
node_modules/karma/lib/index.js generated vendored Normal file
View file

@ -0,0 +1,18 @@
'use strict'
const constants = require('./constants')
const Server = require('./server')
const runner = require('./runner')
const stopper = require('./stopper')
const launcher = require('./launcher')
const cfg = require('./config')
module.exports = {
constants: constants,
VERSION: constants.VERSION,
Server: Server,
runner: runner,
stopper: stopper,
launcher: launcher,
config: { parseConfig: cfg.parseConfig } // lets start with only opening up the `parseConfig` api
}

239
node_modules/karma/lib/init.js generated vendored Executable file
View file

@ -0,0 +1,239 @@
'use strict'
const readline = require('readline')
const path = require('path')
const glob = require('glob')
const mm = require('minimatch')
const exec = require('child_process').exec
const helper = require('./helper')
const logger = require('./logger')
const log = logger.create('init')
const logQueue = require('./init/log-queue')
const StateMachine = require('./init/state_machine')
const COLOR_SCHEME = require('./init/color_schemes')
const formatters = require('./init/formatters')
// TODO(vojta): coverage
// TODO(vojta): html preprocessors
// TODO(vojta): SauceLabs
// TODO(vojta): BrowserStack
let NODE_MODULES_DIR = path.resolve(__dirname, '../..')
// Karma is not in node_modules, probably a symlink,
// use current working dir.
if (!/node_modules$/.test(NODE_MODULES_DIR)) {
NODE_MODULES_DIR = path.resolve('node_modules')
}
function installPackage (pkgName) {
// Do not install if already installed.
try {
require(NODE_MODULES_DIR + '/' + pkgName)
return
} catch (e) {}
log.debug(`Missing plugin "${pkgName}". Installing...`)
const options = {
cwd: path.resolve(NODE_MODULES_DIR, '..')
}
exec(`npm install ${pkgName} --save-dev`, options, function (err, stdout, stderr) {
// Put the logs into the queue and print them after answering current question.
// Otherwise the log would clobber the interactive terminal.
logQueue.push(function () {
if (!err) {
log.debug(`${pkgName} successfully installed.`)
} else if (/is not in the npm registry/.test(stderr)) {
log.warn(`Failed to install "${pkgName}". It is not in the npm registry!\n Please install it manually.`)
} else if (/Error: EACCES/.test(stderr)) {
log.warn(`Failed to install "${pkgName}". No permissions to write in ${options.cwd}!\n Please install it manually.`)
} else {
log.warn(`Failed to install "${pkgName}"\n Please install it manually.`)
}
})
})
}
function validatePattern (pattern) {
if (!glob.sync(pattern).length) {
log.warn('There is no file matching this pattern.\n')
}
}
function validateBrowser (name) {
// TODO(vojta): check if the path resolves to a binary
installPackage('karma-' + name.toLowerCase().replace('headless', '').replace('canary', '') + '-launcher')
}
function validateFramework (name) {
installPackage('karma-' + name)
}
function validateRequireJs (useRequire) {
if (useRequire) {
validateFramework('requirejs')
}
}
var questions = [{
id: 'framework',
question: 'Which testing framework do you want to use ?',
hint: 'Press tab to list possible options. Enter to move to the next question.',
options: ['jasmine', 'mocha', 'qunit', 'nodeunit', 'nunit', ''],
validate: validateFramework
}, {
id: 'requirejs',
question: 'Do you want to use Require.js ?',
hint: 'This will add Require.js plugin.\nPress tab to list possible options. Enter to move to the next question.',
options: ['no', 'yes'],
validate: validateRequireJs,
boolean: true
}, {
id: 'browsers',
question: 'Do you want to capture any browsers automatically ?',
hint: 'Press tab to list possible options. Enter empty string to move to the next question.',
options: ['Chrome', 'ChromeHeadless', 'ChromeCanary', 'Firefox', 'Safari', 'PhantomJS', 'Opera', 'IE', ''],
validate: validateBrowser,
multiple: true
}, {
id: 'files',
question: 'What is the location of your source and test files ?',
hint: 'You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".\nEnter empty string to move to the next question.',
multiple: true,
validate: validatePattern
}, {
id: 'exclude',
question: 'Should any of the files included by the previous patterns be excluded ?',
hint: 'You can use glob patterns, eg. "**/*.swp".\nEnter empty string to move to the next question.',
multiple: true,
validate: validatePattern
}, {
id: 'generateTestMain',
question: 'Do you wanna generate a bootstrap file for RequireJS?',
hint: 'This will generate test-main.js/coffee that configures RequireJS and starts the tests.',
options: ['no', 'yes'],
boolean: true,
condition: (answers) => answers.requirejs
}, {
id: 'includedFiles',
question: 'Which files do you want to include with <script> tag ?',
hint: 'This should be a script that bootstraps your test by configuring Require.js and ' +
'kicking __karma__.start(), probably your test-main.js file.\n' +
'Enter empty string to move to the next question.',
multiple: true,
validate: validatePattern,
condition: (answers) => answers.requirejs && !answers.generateTestMain
}, {
id: 'autoWatch',
question: 'Do you want Karma to watch all the files and run the tests on change ?',
hint: 'Press tab to list possible options.',
options: ['yes', 'no'],
boolean: true
}]
function getBasePath (configFilePath, cwd) {
const configParts = path.dirname(configFilePath).split(path.sep)
const cwdParts = cwd.split(path.sep)
const base = []
while (configParts.length && configParts[0] === cwdParts[0]) {
configParts.shift()
cwdParts.shift()
}
while (configParts.length) {
const part = configParts.shift()
if (part === '..') {
base.unshift(cwdParts.pop())
} else if (part !== '.') {
base.unshift('..')
}
}
return base.join(path.sep)
}
function processAnswers (answers, basePath, testMainFile) {
const processedAnswers = {
basePath: basePath,
files: answers.files,
onlyServedFiles: [],
exclude: answers.exclude,
autoWatch: answers.autoWatch,
generateTestMain: answers.generateTestMain,
browsers: answers.browsers,
frameworks: [],
preprocessors: {}
}
if (answers.framework) {
processedAnswers.frameworks.push(answers.framework)
}
if (answers.requirejs) {
processedAnswers.frameworks.push('requirejs')
processedAnswers.files = answers.includedFiles || []
processedAnswers.onlyServedFiles = answers.files
if (answers.generateTestMain) {
processedAnswers.files.push(testMainFile)
}
}
const allPatterns = answers.files.concat(answers.includedFiles || [])
if (allPatterns.some((pattern) => mm(pattern, '**/*.coffee'))) {
installPackage('karma-coffee-preprocessor')
processedAnswers.preprocessors['**/*.coffee'] = ['coffee']
}
return processedAnswers
}
exports.init = function (config) {
logger.setupFromConfig(config)
const colorScheme = !helper.isDefined(config.colors) || config.colors ? COLOR_SCHEME.ON : COLOR_SCHEME.OFF
// need to be registered before creating readlineInterface
process.stdin.on('keypress', function (s, key) {
sm.onKeypress(key)
})
const rli = readline.createInterface(process.stdin, process.stdout)
const sm = new StateMachine(rli, colorScheme)
rli.on('line', sm.onLine.bind(sm))
// clean colors
rli.on('SIGINT', function () {
sm.kill()
process.exit(0)
})
sm.process(questions, function (answers) {
const cwd = process.cwd()
const configFile = config.configFile || 'karma.conf.js'
const isCoffee = path.extname(configFile) === '.coffee'
const testMainFile = isCoffee ? 'test-main.coffee' : 'test-main.js'
const formatter = formatters.createForPath(configFile)
const processedAnswers = processAnswers(answers, getBasePath(configFile, cwd), testMainFile)
const configFilePath = path.resolve(cwd, configFile)
const testMainFilePath = path.resolve(cwd, testMainFile)
if (isCoffee) {
installPackage('coffeescript')
}
if (processedAnswers.generateTestMain) {
formatter.writeRequirejsConfigFile(testMainFilePath)
console.log(colorScheme.success(`RequireJS bootstrap file generated at "${testMainFilePath}".`))
}
formatter.writeConfigFile(configFilePath, processedAnswers)
console.log(colorScheme.success(`Config file generated at "${configFilePath}".`))
})
}

28
node_modules/karma/lib/init/color_schemes.js generated vendored Normal file
View file

@ -0,0 +1,28 @@
const COLORS_ON = {
RESET: '\x1B[39m',
ANSWER: '\x1B[36m', // NYAN
SUCCESS: '\x1B[32m', // GREEN
QUESTION: '\x1B[1m', // BOLD
question: function (str) {
return this.QUESTION + str + '\x1B[22m'
},
success: function (str) {
return this.SUCCESS + str + this.RESET
}
}
const COLORS_OFF = {
RESET: '',
ANSWER: '',
SUCCESS: '',
QUESTION: '',
question: function (str) {
return str
},
success: function (str) {
return str
}
}
exports.ON = COLORS_ON
exports.OFF = COLORS_OFF

116
node_modules/karma/lib/init/formatters.js generated vendored Normal file
View file

@ -0,0 +1,116 @@
'use strict'
const path = require('path')
const FileUtils = require('../utils/file-utils')
function quote (value) {
return `'${value}'`
}
function formatLine (items) {
return items.map(quote).join(', ')
}
function formatMultiLines (items) {
return items
.map((file) => '\n ' + file)
.join(',')
}
function formatFiles (includedFiles, onlyServedFiles) {
const lines = []
.concat(includedFiles.map(quote))
.concat(onlyServedFiles.map((file) => `{ pattern: ${quote(file)}, included: false }`))
return formatMultiLines(lines)
}
function formatPreprocessors (preprocessors) {
const lines = Object.keys(preprocessors)
.map((pattern) => `${quote(pattern)}: [${formatLine(preprocessors[pattern])}]`)
return formatMultiLines(lines)
}
function getConfigPath (file) {
return path.join(__dirname, `/../../${file}`)
}
class JavaScriptFormatter {
constructor () {
this.TEMPLATE_FILE_PATH = getConfigPath('config.tpl.js')
this.REQUIREJS_TEMPLATE_FILE = getConfigPath('requirejs.config.tpl.js')
}
generateConfigFile (answers) {
const replacements = this.formatAnswers(answers)
return FileUtils
.readFile(this.TEMPLATE_FILE_PATH)
.replace(/%(.*)%/g, (a, key) => replacements[key])
}
writeConfigFile (path, answers) {
FileUtils.saveFile(path, this.generateConfigFile(answers))
}
writeRequirejsConfigFile (path) {
FileUtils.copyFile(this.REQUIREJS_TEMPLATE_FILE, path)
}
formatAnswers (answers) {
return {
DATE: new Date(),
BASE_PATH: answers.basePath,
FRAMEWORKS: formatLine(answers.frameworks),
FILES: formatFiles(answers.files, answers.onlyServedFiles),
EXCLUDE: formatFiles(answers.exclude, []),
AUTO_WATCH: answers.autoWatch ? 'true' : 'false',
BROWSERS: formatLine(answers.browsers),
PREPROCESSORS: formatPreprocessors(answers.preprocessors)
}
}
}
class CoffeeFormatter extends JavaScriptFormatter {
constructor () {
super()
this.TEMPLATE_FILE_PATH = getConfigPath('config.tpl.coffee')
this.REQUIREJS_TEMPLATE_FILE = getConfigPath('requirejs.config.tpl.coffee')
}
}
class LiveFormatter extends JavaScriptFormatter {
constructor () {
super()
this.TEMPLATE_FILE_PATH = getConfigPath('config.tpl.ls')
}
}
class TypeFormatter extends JavaScriptFormatter {
constructor () {
super()
this.TEMPLATE_FILE_PATH = getConfigPath('config.tpl.ts')
}
}
exports.JavaScript = JavaScriptFormatter
exports.Coffee = CoffeeFormatter
exports.Live = LiveFormatter
exports.Type = TypeFormatter
exports.createForPath = function (path) {
if (/\.coffee$/.test(path)) {
return new CoffeeFormatter()
}
if (/\.ls$/.test(path)) {
return new LiveFormatter()
}
if (/\.ts$/.test(path)) {
return new TypeFormatter()
}
return new JavaScriptFormatter()
}

15
node_modules/karma/lib/init/log-queue.js generated vendored Normal file
View file

@ -0,0 +1,15 @@
'use strict'
const logQueue = []
function printLogQueue () {
logQueue.forEach((log) => log())
logQueue.length = 0
}
function push (log) {
logQueue.push(log)
}
module.exports = {
printLogQueue, push
}

143
node_modules/karma/lib/init/state_machine.js generated vendored Normal file
View file

@ -0,0 +1,143 @@
'use strict'
const logQueue = require('./log-queue')
let questions
let currentQuestion
let answers
let currentOptions
let currentOptionsPointer
let currentQuestionId
let done
class StateMachine {
constructor (rli, colors) {
this.rli = rli
this.colors = colors
}
showPrompt () {
this.rli.write(this.colors.ANSWER)
this.rli.prompt()
}
onKeypress (key) {
if (!currentOptions || !key) {
return
}
if (key.name === 'tab' || key.name === 'right' || key.name === 'down') {
this.suggestOption(currentOptionsPointer + 1)
} else if (key.name === 'left' || key.name === 'up') {
this.suggestOption(currentOptionsPointer - 1)
}
if (!key.ctrl && !key.meta && key.name !== 'enter' && key.name !== 'return') {
key.name = 'escape'
}
}
suggestOption (index) {
if (!currentOptions) {
return
}
if (index === -1) {
currentOptionsPointer = currentOptions.length - 1
} else if (index === currentOptions.length) {
currentOptionsPointer = 0
} else {
currentOptionsPointer = index
}
this.rli._deleteLineLeft()
this.rli._deleteLineRight()
this.rli.write(currentOptions[currentOptionsPointer])
}
kill () {
currentOptions = null
currentQuestionId = null
this.rli.write('\n' + this.colors.RESET + '\n')
this.rli.close()
}
onLine (line) {
if (currentQuestionId) {
this.rli.write(this.colors.RESET)
line = line.trim().replace(this.colors.ANSWER, '').replace(this.colors.RESET, '')
if (currentOptions) {
currentOptionsPointer = currentOptions.indexOf(line)
if (currentOptionsPointer === -1) {
return
}
}
if (line === '') {
line = null
}
if (currentQuestion.boolean) {
line = (line === 'yes' || line === 'true' || line === 'on')
}
if (line !== null && currentQuestion.validate) {
currentQuestion.validate(line)
}
if (currentQuestion.multiple) {
answers[currentQuestionId] = answers[currentQuestionId] || []
if (line !== null) {
answers[currentQuestionId].push(line)
this.showPrompt()
if (currentOptions) {
currentOptions.splice(currentOptionsPointer, 1)
currentOptionsPointer = -1
}
} else {
this.nextQuestion()
}
} else {
answers[currentQuestionId] = line
this.nextQuestion()
}
}
}
nextQuestion () {
currentQuestion = questions.shift()
while (currentQuestion && currentQuestion.condition && !currentQuestion.condition(answers)) {
currentQuestion = questions.shift()
}
logQueue.printLogQueue()
if (currentQuestion) {
currentQuestionId = null
this.rli.write('\n' + this.colors.question(currentQuestion.question) + '\n')
this.rli.write(currentQuestion.hint + '\n')
this.showPrompt()
currentOptions = currentQuestion.options || null
currentQuestionId = currentQuestion.id
this.suggestOption(0)
} else {
this.kill()
done(answers)
}
}
process (_questions, _done) {
questions = _questions
answers = {}
done = _done
this.nextQuestion()
}
}
module.exports = StateMachine

199
node_modules/karma/lib/launcher.js generated vendored Normal file
View file

@ -0,0 +1,199 @@
'use strict'
const Jobs = require('qjobs')
const log = require('./logger').create('launcher')
const baseDecorator = require('./launchers/base').decoratorFactory
const captureTimeoutDecorator = require('./launchers/capture_timeout').decoratorFactory
const retryDecorator = require('./launchers/retry').decoratorFactory
const processDecorator = require('./launchers/process').decoratorFactory
// TODO(vojta): remove once nobody uses it
const baseBrowserDecoratorFactory = function (
baseLauncherDecorator,
captureTimeoutLauncherDecorator,
retryLauncherDecorator,
processLauncherDecorator,
processKillTimeout
) {
return function (launcher) {
baseLauncherDecorator(launcher)
captureTimeoutLauncherDecorator(launcher)
retryLauncherDecorator(launcher)
processLauncherDecorator(launcher, processKillTimeout)
}
}
class Launcher {
constructor (server, emitter, injector) {
this._server = server
this._emitter = emitter
this._injector = injector
this._browsers = []
this._lastStartTime = null
// Attach list of dependency injection parameters to methods.
this.launch.$inject = [
'config.browsers',
'config.concurrency'
]
this.launchSingle.$inject = [
'config.protocol',
'config.hostname',
'config.port',
'config.urlRoot',
'config.upstreamProxy',
'config.processKillTimeout'
]
this._emitter.on('exit', (callback) => this.killAll(callback))
}
getBrowserById (id) {
return this._browsers.find((browser) => browser.id === id)
}
launchSingle (protocol, hostname, port, urlRoot, upstreamProxy, processKillTimeout) {
if (upstreamProxy) {
protocol = upstreamProxy.protocol
hostname = upstreamProxy.hostname
port = upstreamProxy.port
urlRoot = upstreamProxy.path + urlRoot.slice(1)
}
return (name) => {
let browser
const locals = {
id: ['value', Launcher.generateId()],
name: ['value', name],
processKillTimeout: ['value', processKillTimeout],
baseLauncherDecorator: ['factory', baseDecorator],
captureTimeoutLauncherDecorator: ['factory', captureTimeoutDecorator],
retryLauncherDecorator: ['factory', retryDecorator],
processLauncherDecorator: ['factory', processDecorator],
baseBrowserDecorator: ['factory', baseBrowserDecoratorFactory]
}
// TODO(vojta): determine script from name
if (name.includes('/')) {
name = 'Script'
}
try {
browser = this._injector.createChild([locals], ['launcher:' + name]).get('launcher:' + name)
} catch (e) {
if (e.message.includes(`No provider for "launcher:${name}"`)) {
log.error(`Cannot load browser "${name}": it is not registered! Perhaps you are missing some plugin?`)
} else {
log.error(`Cannot load browser "${name}"!\n ` + e.stack)
}
this._emitter.emit('load_error', 'launcher', name)
return
}
this.jobs.add((args, done) => {
log.info(`Starting browser ${browser.displayName || browser.name}`)
browser.on('browser_process_failure', () => done(browser.error))
browser.on('done', () => {
if (!browser.error && browser.state !== browser.STATE_RESTARTING) {
done(null, browser)
}
})
browser.start(`${protocol}//${hostname}:${port}${urlRoot}`)
}, [])
this.jobs.run()
this._browsers.push(browser)
}
}
launch (names, concurrency) {
log.info(`Launching browsers ${names.join(', ')} with concurrency ${concurrency === Infinity ? 'unlimited' : concurrency}`)
this.jobs = new Jobs({ maxConcurrency: concurrency })
this._lastStartTime = Date.now()
if (this._server.loadErrors.length) {
this.jobs.add((args, done) => done(), [])
} else {
names.forEach((name) => this._injector.invoke(this.launchSingle, this)(name))
}
this.jobs.on('end', (err) => {
log.debug('Finished all browsers')
if (err) {
log.error(err)
}
})
this.jobs.run()
return this._browsers
}
kill (id, callback) {
callback = callback || function () {}
const browser = this.getBrowserById(id)
if (browser) {
browser.forceKill().then(callback)
return true
}
process.nextTick(callback)
return false
}
restart (id) {
const browser = this.getBrowserById(id)
if (browser) {
browser.restart()
return true
}
return false
}
killAll (callback) {
callback = callback || function () {}
log.debug('Disconnecting all browsers')
if (!this._browsers.length) {
return process.nextTick(callback)
}
Promise.all(
this._browsers
.map((browser) => browser.forceKill())
).then(callback)
}
areAllCaptured () {
return this._browsers.every((browser) => browser.isCaptured())
}
markCaptured (id) {
const browser = this.getBrowserById(id)
if (browser) {
browser.markCaptured()
log.debug(`${browser.name} (id ${browser.id}) captured in ${(Date.now() - this._lastStartTime) / 1000} secs`)
}
}
static generateId () {
return Math.floor(Math.random() * 100000000).toString()
}
}
Launcher.factory = function (server, emitter, injector) {
return new Launcher(server, emitter, injector)
}
Launcher.factory.$inject = ['server', 'emitter', 'injector']
exports.Launcher = Launcher

140
node_modules/karma/lib/launchers/base.js generated vendored Normal file
View file

@ -0,0 +1,140 @@
const KarmaEventEmitter = require('../events').EventEmitter
const EventEmitter = require('events').EventEmitter
const log = require('../logger').create('launcher')
const helper = require('../helper')
const BEING_CAPTURED = 'BEING_CAPTURED'
const CAPTURED = 'CAPTURED'
const BEING_KILLED = 'BEING_KILLED'
const FINISHED = 'FINISHED'
const RESTARTING = 'RESTARTING'
const BEING_FORCE_KILLED = 'BEING_FORCE_KILLED'
/**
* Base launcher that any custom launcher extends.
*/
function BaseLauncher (id, emitter) {
if (this.start) {
return
}
// TODO(vojta): figure out how to do inheritance with DI
Object.keys(EventEmitter.prototype).forEach(function (method) {
this[method] = EventEmitter.prototype[method]
}, this)
this.bind = KarmaEventEmitter.prototype.bind.bind(this)
this.emitAsync = KarmaEventEmitter.prototype.emitAsync.bind(this)
this.id = id
this._state = null
Object.defineProperty(this, 'state', {
get: () => {
return this._state
},
set: (toState) => {
log.debug(`${this._state} -> ${toState}`)
this._state = toState
}
})
this.error = null
let killingPromise
let previousUrl
this.start = function (url) {
previousUrl = url
this.error = null
this.state = BEING_CAPTURED
this.emit('start', url + '?id=' + this.id + (helper.isDefined(this.displayName) ? '&displayName=' + encodeURIComponent(this.displayName) : ''))
}
this.kill = function () {
// Already killed, or being killed.
if (killingPromise) {
return killingPromise
}
killingPromise = this.emitAsync('kill').then(() => {
this.state = FINISHED
})
this.state = BEING_KILLED
return killingPromise
}
this.forceKill = function () {
this.kill()
this.state = BEING_FORCE_KILLED
return killingPromise
}
this.restart = function () {
if (this.state === BEING_FORCE_KILLED) {
return
}
if (!killingPromise) {
killingPromise = this.emitAsync('kill')
}
killingPromise.then(() => {
if (this.state === BEING_FORCE_KILLED) {
this.state = FINISHED
} else {
killingPromise = null
log.debug(`Restarting ${this.name}`)
this.start(previousUrl)
}
})
this.state = RESTARTING
}
this.markCaptured = function () {
if (this.state === BEING_CAPTURED) {
this.state = CAPTURED
}
}
this.isCaptured = function () {
return this.state === CAPTURED
}
this.toString = function () {
return this.name
}
this._done = function (error) {
killingPromise = killingPromise || Promise.resolve()
this.error = this.error || error
this.emit('done')
if (this.error && this.state !== BEING_FORCE_KILLED && this.state !== RESTARTING) {
emitter.emit('browser_process_failure', this)
}
this.state = FINISHED
}
this.STATE_BEING_CAPTURED = BEING_CAPTURED
this.STATE_CAPTURED = CAPTURED
this.STATE_BEING_KILLED = BEING_KILLED
this.STATE_FINISHED = FINISHED
this.STATE_RESTARTING = RESTARTING
this.STATE_BEING_FORCE_KILLED = BEING_FORCE_KILLED
}
BaseLauncher.decoratorFactory = function (id, emitter) {
return function (launcher) {
BaseLauncher.call(launcher, id, emitter)
}
}
module.exports = BaseLauncher

43
node_modules/karma/lib/launchers/capture_timeout.js generated vendored Normal file
View file

@ -0,0 +1,43 @@
const log = require('../logger').create('launcher')
/**
* Kill browser if it does not capture in given `captureTimeout`.
*/
function CaptureTimeoutLauncher (timer, captureTimeout) {
if (!captureTimeout) {
return
}
let pendingTimeoutId = null
this.on('start', () => {
pendingTimeoutId = timer.setTimeout(() => {
pendingTimeoutId = null
if (this.state !== this.STATE_BEING_CAPTURED) {
return
}
log.warn(`${this.name} has not captured in ${captureTimeout} ms, killing.`)
this.error = 'timeout'
this.kill()
}, captureTimeout)
})
this.on('done', () => {
if (pendingTimeoutId) {
timer.clearTimeout(pendingTimeoutId)
pendingTimeoutId = null
}
})
}
CaptureTimeoutLauncher.decoratorFactory = function (timer,
/* config.captureTimeout */ captureTimeout) {
return function (launcher) {
CaptureTimeoutLauncher.call(launcher, timer, captureTimeout)
}
}
CaptureTimeoutLauncher.decoratorFactory.$inject = ['timer', 'config.captureTimeout']
module.exports = CaptureTimeoutLauncher

185
node_modules/karma/lib/launchers/process.js generated vendored Normal file
View file

@ -0,0 +1,185 @@
const path = require('path')
const log = require('../logger').create('launcher')
const env = process.env
function ProcessLauncher (spawn, tempDir, timer, processKillTimeout) {
const self = this
let onExitCallback
const killTimeout = processKillTimeout || 2000
// Will hold output from the spawned child process
const streamedOutputs = {
stdout: '',
stderr: ''
}
this._tempDir = tempDir.getPath(`/karma-${this.id.toString()}`)
this.on('start', function (url) {
tempDir.create(self._tempDir)
self._start(url)
})
this.on('kill', function (done) {
if (!self._process) {
return process.nextTick(done)
}
onExitCallback = done
self._process.kill()
self._killTimer = timer.setTimeout(self._onKillTimeout, killTimeout)
})
this._start = function (url) {
self._execCommand(self._getCommand(), self._getOptions(url))
}
this._getCommand = function () {
return env[self.ENV_CMD] || self.DEFAULT_CMD[process.platform]
}
this._getOptions = function (url) {
return [url]
}
// Normalize the command, remove quotes (spawn does not like them).
this._normalizeCommand = function (cmd) {
if (cmd.charAt(0) === cmd.charAt(cmd.length - 1) && '\'`"'.includes(cmd.charAt(0))) {
cmd = cmd.slice(1, -1)
log.warn(`The path should not be quoted.\n Normalized the path to ${cmd}`)
}
return path.normalize(cmd)
}
this._onStdout = function (data) {
streamedOutputs.stdout += data
}
this._onStderr = function (data) {
streamedOutputs.stderr += data
}
this._execCommand = function (cmd, args) {
if (!cmd) {
log.error(`No binary for ${self.name} browser on your platform.\n Please, set "${self.ENV_CMD}" env variable.`)
// disable restarting
self._retryLimit = -1
return self._clearTempDirAndReportDone('no binary')
}
cmd = this._normalizeCommand(cmd)
log.debug(cmd + ' ' + args.join(' '))
self._process = spawn(cmd, args)
let errorOutput = ''
self._process.stdout.on('data', self._onStdout)
self._process.stderr.on('data', self._onStderr)
self._process.on('exit', function (code, signal) {
self._onProcessExit(code, signal, errorOutput)
})
self._process.on('error', function (err) {
if (err.code === 'ENOENT') {
self._retryLimit = -1
errorOutput = `Can not find the binary ${cmd}\n\tPlease set env variable ${self.ENV_CMD}`
} else if (err.code === 'EACCES') {
self._retryLimit = -1
errorOutput = `Permission denied accessing the binary ${cmd}\n\tMaybe it's a directory?`
} else {
errorOutput += err.toString()
}
self._onProcessExit(-1, null, errorOutput)
})
self._process.stderr.on('data', function (errBuff) {
errorOutput += errBuff.toString()
})
}
this._onProcessExit = function (code, signal, errorOutput) {
if (!self._process) {
// Both exit and error events trigger _onProcessExit(), but we only need one cleanup.
return
}
log.debug(`Process ${self.name} exited with code ${code} and signal ${signal}`)
let error = null
if (self.state === self.STATE_BEING_CAPTURED) {
log.error(`Cannot start ${self.name}\n\t${errorOutput}`)
error = 'cannot start'
}
if (self.state === self.STATE_CAPTURED) {
log.error(`${self.name} crashed.\n\t${errorOutput}`)
error = 'crashed'
}
if (error) {
log.error(`${self.name} stdout: ${streamedOutputs.stdout}`)
log.error(`${self.name} stderr: ${streamedOutputs.stderr}`)
}
self._process = null
streamedOutputs.stdout = ''
streamedOutputs.stderr = ''
if (self._killTimer) {
timer.clearTimeout(self._killTimer)
self._killTimer = null
}
self._clearTempDirAndReportDone(error)
}
this._clearTempDirAndReportDone = function (error) {
tempDir.remove(self._tempDir, function () {
self._done(error)
if (onExitCallback) {
onExitCallback()
onExitCallback = null
}
})
}
this._onKillTimeout = function () {
if (self.state !== self.STATE_BEING_KILLED && self.state !== self.STATE_BEING_FORCE_KILLED) {
return
}
log.warn(`${self.name} was not killed in ${killTimeout} ms, sending SIGKILL.`)
self._process.kill('SIGKILL')
// NOTE: https://github.com/karma-runner/karma/pull/1184
// NOTE: SIGKILL is just a signal. Processes should never ignore it, but they can.
// If a process gets into a state where it doesn't respond in a reasonable amount of time
// Karma should warn, and continue as though the kill succeeded.
// This a certainly suboptimal, but it is better than having the test harness hang waiting
// for a zombie child process to exit.
self._killTimer = timer.setTimeout(function () {
log.warn(`${self.name} was not killed by SIGKILL in ${killTimeout} ms, continuing.`)
self._onProcessExit(-1, null, '')
}, killTimeout)
}
}
ProcessLauncher.decoratorFactory = function (timer) {
return function (launcher, processKillTimeout) {
const spawn = require('child_process').spawn
function spawnWithoutOutput () {
const proc = spawn.apply(null, arguments)
proc.stdout.resume()
proc.stderr.resume()
return proc
}
ProcessLauncher.call(launcher, spawnWithoutOutput, require('../temp_dir'), timer, processKillTimeout)
}
}
module.exports = ProcessLauncher

31
node_modules/karma/lib/launchers/retry.js generated vendored Normal file
View file

@ -0,0 +1,31 @@
const log = require('../logger').create('launcher')
function RetryLauncher (retryLimit) {
this._retryLimit = retryLimit
this.on('done', () => {
if (!this.error) {
return
}
if (this._retryLimit > 0) {
log.info(`Trying to start ${this.name} again (${retryLimit - this._retryLimit + 1}/${retryLimit}).`)
this.restart()
this._retryLimit--
} else if (this._retryLimit === 0) {
log.error(`${this.name} failed ${retryLimit} times (${this.error}). Giving up.`)
} else {
log.debug(`${this.name} failed (${this.error}). Not restarting.`)
}
})
}
RetryLauncher.decoratorFactory = function (retryLimit) {
return function (launcher) {
RetryLauncher.call(launcher, retryLimit)
}
}
RetryLauncher.decoratorFactory.$inject = ['config.retryLimit']
module.exports = RetryLauncher

115
node_modules/karma/lib/logger.js generated vendored Normal file
View file

@ -0,0 +1,115 @@
// This is the **logger** module for *Karma*. It uses
// [log4js](https://github.com/nomiddlename/log4js-node) to handle and
// configure all logging that happens inside of *Karma*.
// ### Helpers and Setup
let log4js = require('log4js')
const helper = require('./helper')
const constant = require('./constants')
// #### Public Functions
// Setup the logger by passing in the configuration options. It needs
// three arguments:
//
// setup(logLevel, colors, appenders)
//
// * `logLevel`: *String* Defines the global log level.
// * `colors`: *Boolean* Use colors in the stdout or not.
// * `appenders`: *Object* This will be passed as appenders to log4js
// to allow for fine grained configuration of log4js. For more information
// see https://github.com/nomiddlename/log4js-node.
// *Array* is also accepted for backwards compatibility.
function setup (level, colors, appenders) {
// Turn color on/off on the console appenders with pattern layout
const pattern = colors ? constant.COLOR_PATTERN : constant.NO_COLOR_PATTERN
if (appenders) {
// Convert Array to Object for backwards compatibility.
if (appenders.map) {
if (appenders.length === 0) {
appenders = [constant.CONSOLE_APPENDER]
}
const v1Appenders = appenders
appenders = {}
v1Appenders.forEach(function (appender, index) {
if (appender.type === 'console') {
appenders.console = appender
if (helper.isDefined(appender.layout) && appender.layout.type === 'pattern') {
appender.layout.pattern = pattern
}
} else {
appenders[index + ''] = appender
}
return appender
})
}
} else {
appenders = { console: constant.CONSOLE_APPENDER }
}
log4js.configure({
appenders: appenders,
categories: {
default: {
appenders: Object.keys(appenders),
level: level
}
}
})
}
// Setup the logger by passing in the config object. The function sets the
// `colors` and `logLevel` if they are defined. It takes two arguments:
//
// setupFromConfig(config, appenders)
//
// * `config`: *Object* The configuration object.
// * `appenders`: *Object* This will be passed as appenders to log4js
// to allow for fine grained configuration of log4js. For more information
// see https://github.com/nomiddlename/log4js-node.
// *Array* is also accepted for backwards compatibility.
function setupFromConfig (config, appenders) {
let useColors = true
let logLevel = constant.LOG_INFO
if (helper.isDefined(config.colors)) {
useColors = config.colors
}
if (helper.isDefined(config.logLevel)) {
logLevel = config.logLevel
}
setup(logLevel, useColors, appenders)
}
const loggerCache = {}
// Create a new logger. There are two optional arguments
// * `name`, which defaults to `karma` and
// If the `name = 'socket.io'` this will create a special wrapper
// to be used as a logger for socket.io.
// * `level`, which defaults to the global level.
function create (name, level) {
name = name || 'karma'
let logger
if (Object.prototype.hasOwnProperty.call(loggerCache, name)) {
logger = loggerCache[name]
} else {
logger = log4js.getLogger(name)
loggerCache[name] = logger
}
if (helper.isDefined(level)) {
logger.setLevel(level)
}
return logger
}
// #### Publish
exports.create = create
exports.setup = setup
exports.setupFromConfig = setupFromConfig
exports._rebindLog4js4testing = function (mockLog4js) {
log4js = mockLog4js
}

105
node_modules/karma/lib/middleware/common.js generated vendored Normal file
View file

@ -0,0 +1,105 @@
/**
* This module contains some common helpers shared between middlewares
*/
'use strict'
const mime = require('mime')
const parseRange = require('range-parser')
const log = require('../logger').create('web-server')
function createServeFile (fs, directory, config) {
const cache = Object.create(null)
return function (filepath, rangeHeader, response, transform, content, doNotCache) {
let responseData
function convertForRangeRequest () {
const range = parseRange(responseData.length, rangeHeader)
if (range === -2) {
return 200 // malformed header string
} else if (range === -1) {
responseData = Buffer.alloc(0) // unsatisfiable range
return 416
} else if (range.type === 'bytes') {
responseData = Buffer.from(responseData)
if (range.length === 1) {
const { start, end } = range[0]
response.setHeader('Content-Range', `bytes ${start}-${end}/${responseData.length}`)
response.setHeader('Accept-Ranges', 'bytes')
response.setHeader('Content-Length', end - start + 1)
responseData = responseData.slice(start, end + 1)
return 206
} else {
responseData = Buffer.alloc(0) // Multiple ranges are not supported. Maybe future?
return 416
}
}
return 200 // All other states, ignore
}
if (directory) {
filepath = directory + filepath
}
if (!content && cache[filepath]) {
content = cache[filepath]
}
if (config && config.customHeaders && config.customHeaders.length > 0) {
config.customHeaders.forEach((header) => {
const regex = new RegExp(header.match)
if (regex.test(filepath)) {
log.debug(`setting header: ${header.name} for: ${filepath}`)
response.setHeader(header.name, header.value)
}
})
}
if (content && !doNotCache) {
log.debug(`serving (cached): ${filepath}`)
response.setHeader('Content-Type', mime.getType(filepath, 'text/plain'))
responseData = (transform && transform(content)) || content
response.writeHead(rangeHeader ? convertForRangeRequest() : 200)
return response.end(responseData)
}
return fs.readFile(filepath, function (error, data) {
if (error) {
return serve404(response, filepath)
}
if (!doNotCache) {
cache[filepath] = data.toString()
}
log.debug('serving: ' + filepath)
response.setHeader('Content-Type', mime.getType(filepath, 'text/plain'))
responseData = (transform && transform(data.toString())) || data
response.writeHead(rangeHeader ? convertForRangeRequest() : 200)
return response.end(responseData)
})
}
}
function serve404 (response, path) {
log.warn(`404: ${path}`)
response.writeHead(404)
return response.end('NOT FOUND')
}
function setNoCacheHeaders (response) {
response.setHeader('Cache-Control', 'no-cache')
response.setHeader('Pragma', 'no-cache')
response.setHeader('Expires', (new Date(0)).toUTCString())
}
function setHeavyCacheHeaders (response) {
response.setHeader('Cache-Control', 'public, max-age=31536000')
}
// PUBLIC API
exports.createServeFile = createServeFile
exports.setNoCacheHeaders = setNoCacheHeaders
exports.setHeavyCacheHeaders = setHeavyCacheHeaders
exports.serve404 = serve404

259
node_modules/karma/lib/middleware/karma.js generated vendored Normal file
View file

@ -0,0 +1,259 @@
/**
* Karma middleware is responsible for serving:
* - client.html (the entrypoint for capturing a browser)
* - debug.html
* - context.html (the execution context, loaded within an iframe)
* - karma.js
*
* The main part is generating context.html, as it contains:
* - generating mappings
* - including <script> and <link> tags
* - setting propert caching headers
*/
const url = require('url')
const log = require('../logger').create('middleware:karma')
const stripHost = require('./strip_host').stripHost
const common = require('./common')
const VERSION = require('../constants').VERSION
const SCRIPT_TYPE = {
js: 'text/javascript',
module: 'module'
}
const FILE_TYPES = [
'css',
'html',
'js',
'module',
'dom'
]
function filePathToUrlPath (filePath, basePath, urlRoot, proxyPath) {
if (filePath.startsWith(basePath)) {
return proxyPath + urlRoot.slice(1) + 'base' + filePath.slice(basePath.length)
}
return proxyPath + urlRoot.slice(1) + 'absolute' + filePath
}
function getQuery (urlStr) {
// eslint-disable-next-line node/no-deprecated-api
return url.parse(urlStr, true).query || {}
}
function getXUACompatibleMetaElement (url) {
const query = getQuery(url)
if (query['x-ua-compatible']) {
return `<meta http-equiv="X-UA-Compatible" content="${query['x-ua-compatible']}"/>`
}
return ''
}
function getXUACompatibleUrl (url) {
const query = getQuery(url)
if (query['x-ua-compatible']) {
return '?x-ua-compatible=' + encodeURIComponent(query['x-ua-compatible'])
}
return ''
}
function createKarmaMiddleware (
filesPromise,
serveStaticFile,
serveFile,
injector,
basePath,
urlRoot,
upstreamProxy,
browserSocketTimeout
) {
const proxyPath = upstreamProxy ? upstreamProxy.path : '/'
return function (request, response, next) {
// These config values should be up to date on every request
const client = injector.get('config.client')
const customContextFile = injector.get('config.customContextFile')
const customDebugFile = injector.get('config.customDebugFile')
const customClientContextFile = injector.get('config.customClientContextFile')
const includeCrossOriginAttribute = injector.get('config.crossOriginAttribute')
const normalizedUrl = stripHost(request.url) || request.url
// For backwards compatibility in middleware plugins, remove in v4.
request.normalizedUrl = normalizedUrl
let requestUrl = normalizedUrl.replace(/\?.*/, '')
const requestedRangeHeader = request.headers.range
// redirect /__karma__ to /__karma__ (trailing slash)
if (requestUrl === urlRoot.slice(0, -1)) {
response.setHeader('Location', proxyPath + urlRoot.slice(1))
response.writeHead(301)
return response.end('MOVED PERMANENTLY')
}
// ignore urls outside urlRoot
if (!requestUrl.startsWith(urlRoot)) {
return next()
}
// remove urlRoot prefix
requestUrl = requestUrl.slice(urlRoot.length - 1)
// serve client.html
if (requestUrl === '/') {
// redirect client_with_context.html
if (!client.useIframe && client.runInParent) {
requestUrl = '/client_with_context.html'
} else { // serve client.html
return serveStaticFile('/client.html', requestedRangeHeader, response, (data) =>
data
.replace('%X_UA_COMPATIBLE%', getXUACompatibleMetaElement(request.url))
.replace('%X_UA_COMPATIBLE_URL%', getXUACompatibleUrl(request.url)))
}
}
if (['/karma.js', '/context.js', '/debug.js'].includes(requestUrl)) {
return serveStaticFile(requestUrl, requestedRangeHeader, response, (data) =>
data
.replace('%KARMA_URL_ROOT%', urlRoot)
.replace('%KARMA_VERSION%', VERSION)
.replace('%KARMA_PROXY_PATH%', proxyPath)
.replace('%BROWSER_SOCKET_TIMEOUT%', browserSocketTimeout))
}
// serve the favicon
if (requestUrl === '/favicon.ico') {
return serveStaticFile(requestUrl, requestedRangeHeader, response)
}
// serve context.html - execution context within the iframe
// or debug.html - execution context without channel to the server
const isRequestingContextFile = requestUrl === '/context.html'
const isRequestingDebugFile = requestUrl === '/debug.html'
const isRequestingClientContextFile = requestUrl === '/client_with_context.html'
if (isRequestingContextFile || isRequestingDebugFile || isRequestingClientContextFile) {
return filesPromise.then((files) => {
let fileServer
let requestedFileUrl
log.debug('custom files', customContextFile, customDebugFile, customClientContextFile)
if (isRequestingContextFile && customContextFile) {
log.debug(`Serving customContextFile ${customContextFile}`)
fileServer = serveFile
requestedFileUrl = customContextFile
} else if (isRequestingDebugFile && customDebugFile) {
log.debug(`Serving customDebugFile ${customDebugFile}`)
fileServer = serveFile
requestedFileUrl = customDebugFile
} else if (isRequestingClientContextFile && customClientContextFile) {
log.debug(`Serving customClientContextFile ${customClientContextFile}`)
fileServer = serveFile
requestedFileUrl = customClientContextFile
} else {
log.debug(`Serving static request ${requestUrl}`)
fileServer = serveStaticFile
requestedFileUrl = requestUrl
}
fileServer(requestedFileUrl, requestedRangeHeader, response, function (data) {
common.setNoCacheHeaders(response)
const scriptTags = []
for (const file of files.included) {
let filePath = file.path
const fileType = file.type || file.detectType()
if (!FILE_TYPES.includes(fileType)) {
if (file.type == null) {
log.warn(
'Unable to determine file type from the file extension, defaulting to js.\n' +
` To silence the warning specify a valid type for ${file.originalPath} in the configuration file.\n` +
' See https://karma-runner.github.io/latest/config/files.html'
)
} else {
log.warn(`Invalid file type (${file.type || 'empty string'}), defaulting to js.`)
}
}
if (!file.isUrl) {
filePath = filePathToUrlPath(filePath, basePath, urlRoot, proxyPath)
if (requestUrl === '/context.html') {
filePath += '?' + file.sha
}
}
const integrityAttribute = file.integrity ? ` integrity="${file.integrity}"` : ''
const crossOriginAttribute = includeCrossOriginAttribute ? ' crossorigin="anonymous"' : ''
if (fileType === 'css') {
scriptTags.push(`<link type="text/css" href="${filePath}" rel="stylesheet"${integrityAttribute}${crossOriginAttribute}>`)
} else if (fileType === 'dom') {
scriptTags.push(file.content)
} else if (fileType === 'html') {
scriptTags.push(`<link href="${filePath}" rel="import"${integrityAttribute}${crossOriginAttribute}>`)
} else {
const scriptType = (SCRIPT_TYPE[fileType] || 'text/javascript')
if (fileType === 'module') {
scriptTags.push(`<script onerror="throw 'Error loading ${filePath}'" type="${scriptType}" src="${filePath}"${integrityAttribute}${crossOriginAttribute}></script>`)
} else {
scriptTags.push(`<script type="${scriptType}" src="${filePath}"${integrityAttribute}${crossOriginAttribute}></script>`)
}
}
}
const scriptUrls = []
// For client_with_context, html elements are not added directly through an iframe.
// Instead, scriptTags is stored to window.__karma__.scriptUrls first. Later, the
// client will read window.__karma__.scriptUrls and dynamically add them to the DOM
// using DOMParser.
if (requestUrl === '/client_with_context.html') {
for (const script of scriptTags) {
scriptUrls.push(
// Escape characters with special roles (tags) in HTML. Open angle brackets are parsed as tags
// immediately, even if it is within double quotations in browsers
script.replace(/</g, '\\x3C').replace(/>/g, '\\x3E'))
}
}
const mappings = data.includes('%MAPPINGS%') ? files.served.map((file) => {
const filePath = filePathToUrlPath(file.path, basePath, urlRoot, proxyPath)
.replace(/\\/g, '\\\\') // Windows paths contain backslashes and generate bad IDs if not escaped
.replace(/'/g, '\\\'') // Escape single quotes - double quotes should not be allowed!
return ` '${filePath}': '${file.sha}'`
}) : []
return data
.replace('%SCRIPTS%', () => scriptTags.join('\n'))
.replace('%CLIENT_CONFIG%', 'window.__karma__.config = ' + JSON.stringify(client) + ';\n')
.replace('%SCRIPT_URL_ARRAY%', () => 'window.__karma__.scriptUrls = ' + JSON.stringify(scriptUrls) + ';\n')
.replace('%MAPPINGS%', () => 'window.__karma__.files = {\n' + mappings.join(',\n') + '\n};\n')
.replace('%X_UA_COMPATIBLE%', getXUACompatibleMetaElement(request.url))
})
})
} else if (requestUrl === '/context.json') {
return filesPromise.then((files) => {
common.setNoCacheHeaders(response)
response.writeHead(200)
response.end(JSON.stringify({
files: files.included.map((file) => filePathToUrlPath(file.path + '?' + file.sha, basePath, urlRoot, proxyPath))
}))
})
}
return next()
}
}
createKarmaMiddleware.$inject = [
'filesPromise',
'serveStaticFile',
'serveFile',
'injector',
'config.basePath',
'config.urlRoot',
'config.upstreamProxy',
'config.browserSocketTimeout'
]
// PUBLIC API
exports.create = createKarmaMiddleware

130
node_modules/karma/lib/middleware/proxy.js generated vendored Normal file
View file

@ -0,0 +1,130 @@
const url = require('url')
const { Agent: httpAgent } = require('http')
const { Agent: httpsAgent } = require('https')
const httpProxy = require('http-proxy')
const _ = require('lodash')
const { lookup } = require('../utils/dns-utils')
const log = require('../logger').create('proxy')
function parseProxyConfig (proxies, config) {
proxies = proxies || []
return _.sortBy(_.map(proxies, function (proxyConfiguration, proxyPath) {
if (typeof proxyConfiguration === 'string') {
proxyConfiguration = { target: proxyConfiguration }
}
let proxyUrl = proxyConfiguration.target
// eslint-disable-next-line node/no-deprecated-api
const proxyDetails = url.parse(proxyUrl)
let pathname = proxyDetails.pathname
if (proxyPath.endsWith('/') && !proxyUrl.endsWith('/')) {
log.warn(`proxy "${proxyUrl}" normalized to "${proxyUrl}/"`)
proxyUrl += '/'
pathname += '/'
}
if (!proxyPath.endsWith('/') && proxyUrl.endsWith('/')) {
log.warn(`proxy "${proxyPath}" normalized to "${proxyPath}/"`)
proxyPath += '/'
}
if (pathname === '/' && !proxyUrl.endsWith('/')) {
pathname = ''
}
const hostname = proxyDetails.hostname || config.hostname
const protocol = proxyDetails.protocol || config.protocol
const defaultPorts = {
'http:': '80',
'https:': '443'
}
const port = proxyDetails.port || defaultPorts[proxyDetails.protocol] || config.port
const changeOrigin = proxyConfiguration.changeOrigin || false
const Agent = protocol === 'https:' ? httpsAgent : httpAgent
const agent = new Agent({
keepAlive: true,
lookup
})
const proxy = httpProxy.createProxyServer({
target: { host: hostname, port, protocol },
xfwd: true,
changeOrigin: changeOrigin,
secure: config.proxyValidateSSL,
agent
})
;['proxyReq', 'proxyRes'].forEach(function (name) {
const callback = proxyDetails[name] || config[name]
if (callback) {
proxy.on(name, callback)
}
})
proxy.on('error', function proxyError (err, req, res) {
if (err.code === 'ECONNRESET' && req.socket.destroyed) {
log.debug(`failed to proxy ${req.url} (browser hung up the socket)`)
} else {
log.warn(`failed to proxy ${req.url} (${err.message})`)
}
res.destroy()
})
return { path: proxyPath, baseUrl: pathname, host: hostname, port, proxy, agent }
}), 'path').reverse()
}
/**
* Returns a handler which understands the proxies and its redirects, along with the proxy to use
* @param proxies An array of proxy record objects
* @param urlRoot The URL root that karma is mounted on
* @return {Function} handler function
*/
function createProxyHandler (proxies, urlRoot) {
if (!proxies.length) {
const nullProxy = (request, response, next) => next()
nullProxy.upgrade = () => {}
return nullProxy
}
function createProxy (request, response, next) {
const proxyRecord = proxies.find((p) => request.url.startsWith(p.path))
if (proxyRecord) {
log.debug(`proxying request - ${request.url} to ${proxyRecord.host}:${proxyRecord.port}`)
request.url = request.url.replace(proxyRecord.path, proxyRecord.baseUrl)
proxyRecord.proxy.web(request, response)
} else {
return next()
}
}
createProxy.upgrade = function (request, socket, head) {
// special-case karma's route to avoid upgrading it
if (request.url.startsWith(urlRoot)) {
log.debug(`NOT upgrading proxyWebSocketRequest ${request.url}`)
return
}
const proxyRecord = proxies.find((p) => request.url.startsWith(p.path))
if (proxyRecord) {
log.debug(`upgrade proxyWebSocketRequest ${request.url} to ${proxyRecord.host}:${proxyRecord.port}`)
request.url = request.url.replace(proxyRecord.path, proxyRecord.baseUrl)
proxyRecord.proxy.ws(request, socket, head)
}
}
return createProxy
}
exports.create = function (/* config */config, /* config.proxies */proxies, /* emitter */emitter) {
const proxyRecords = parseProxyConfig(proxies, config)
emitter.on('exit', (done) => {
log.debug('Destroying proxy agents')
proxyRecords.forEach((proxyRecord) => {
proxyRecord.agent.destroy()
})
done()
})
return createProxyHandler(proxyRecords, config.urlRoot)
}

114
node_modules/karma/lib/middleware/runner.js generated vendored Normal file
View file

@ -0,0 +1,114 @@
/**
* Runner middleware is responsible for communication with `karma run`.
*
* It basically triggers a test run and streams stdout back.
*/
const _ = require('lodash')
const path = require('path')
const helper = require('../helper')
const log = require('../logger').create()
const constant = require('../constants')
const json = require('body-parser').json()
// TODO(vojta): disable when single-run mode
function createRunnerMiddleware (emitter, fileList, capturedBrowsers, reporter, executor,
/* config.protocol */ protocol, /* config.hostname */ hostname, /* config.port */
port, /* config.urlRoot */ urlRoot, config) {
helper.saveOriginalArgs(config)
return function (request, response, next) {
if (request.url !== '/__run__' && request.url !== urlRoot + 'run') {
return next()
}
log.debug('Execution (fired by runner)')
response.writeHead(200)
if (!capturedBrowsers.length) {
const url = `${protocol}//${hostname}:${port}${urlRoot}`
return response.end(`No captured browser, open ${url}\n`)
}
json(request, response, function () {
if (!capturedBrowsers.areAllReady([])) {
response.write('Waiting for previous execution...\n')
}
const data = request.body
updateClientArgs(data)
handleRun(data)
refreshFileList(data).then(() => {
executor.schedule()
}).catch((error) => {
const errorMessage = `Error during refresh file list. ${error.stack || error}`
executor.scheduleError(errorMessage)
})
})
function updateClientArgs (data) {
helper.restoreOriginalArgs(config)
if (_.isEmpty(data.args)) {
log.debug('Ignoring empty client.args from run command')
} else if ((_.isArray(data.args) && _.isArray(config.client.args)) ||
(_.isPlainObject(data.args) && _.isPlainObject(config.client.args))) {
log.debug('Merging client.args with ', data.args)
config.client.args = _.merge(config.client.args, data.args)
} else {
log.warn('Replacing client.args with ', data.args, ' as their types do not match.')
config.client.args = data.args
}
}
async function refreshFileList (data) {
let fullRefresh = true
if (helper.isArray(data.changedFiles)) {
await Promise.all(data.changedFiles.map(async function (filepath) {
await fileList.changeFile(path.resolve(config.basePath, filepath))
fullRefresh = false
}))
}
if (helper.isArray(data.addedFiles)) {
await Promise.all(data.addedFiles.map(async function (filepath) {
await fileList.addFile(path.resolve(config.basePath, filepath))
fullRefresh = false
}))
}
if (helper.isArray(data.removedFiles)) {
await Promise.all(data.removedFiles.map(async function (filepath) {
await fileList.removeFile(path.resolve(config.basePath, filepath))
fullRefresh = false
}))
}
if (fullRefresh && data.refresh !== false) {
log.debug('Refreshing all the files / patterns')
await fileList.refresh()
}
}
function handleRun (data) {
emitter.once('run_start', function () {
const responseWrite = response.write.bind(response)
responseWrite.colors = data.colors
reporter.addAdapter(responseWrite)
// clean up, close runner response
emitter.once('run_complete', function (_browsers, results) {
reporter.removeAdapter(responseWrite)
const emptyTestSuite = (results.failed + results.success) === 0 ? 0 : 1
response.end(constant.EXIT_CODE + emptyTestSuite + results.exitCode)
})
})
}
}
}
createRunnerMiddleware.$inject = ['emitter', 'fileList', 'capturedBrowsers', 'reporter', 'executor',
'config.protocol', 'config.hostname', 'config.port', 'config.urlRoot', 'config']
// PUBLIC API
exports.create = createRunnerMiddleware

67
node_modules/karma/lib/middleware/source_files.js generated vendored Normal file
View file

@ -0,0 +1,67 @@
'use strict'
const querystring = require('querystring')
const common = require('./common')
const log = require('../logger').create('middleware:source-files')
function findByPath (files, path) {
return Array.from(files).find((file) => file.path === path)
}
function composeUrl (url, basePath, urlRoot) {
return url
.replace(urlRoot, '/')
.replace(/\?.*$/, '')
.replace(/^\/absolute/, '')
.replace(/^\/base/, basePath)
}
// Source Files middleware is responsible for serving all the source files under the test.
function createSourceFilesMiddleware (filesPromise, serveFile, basePath, urlRoot) {
return function (request, response, next) {
const requestedFilePath = composeUrl(request.url, basePath, urlRoot)
// When a path contains HTML-encoded characters (e.g %2F used by Jenkins for branches with /)
const requestedFilePathUnescaped = composeUrl(querystring.unescape(request.url), basePath, urlRoot)
request.pause()
log.debug(`Requesting ${request.url}`)
log.debug(`Fetching ${requestedFilePath}`)
return filesPromise.then(function (files) {
// TODO(vojta): change served to be a map rather then an array
const file = findByPath(files.served, requestedFilePath) || findByPath(files.served, requestedFilePathUnescaped)
const rangeHeader = request.headers.range
if (file) {
const acceptEncodingHeader = request.headers['accept-encoding']
const matchedEncoding = Object.keys(file.encodings).find(
(encoding) => new RegExp(`(^|.*, ?)${encoding}(,|$)`).test(acceptEncodingHeader)
)
const content = file.encodings[matchedEncoding] || file.content
serveFile(file.contentPath || file.path, rangeHeader, response, function () {
if (/\?\w+/.test(request.url)) {
common.setHeavyCacheHeaders(response) // files with timestamps - cache one year, rely on timestamps
} else {
common.setNoCacheHeaders(response) // without timestamps - no cache (debug)
}
if (matchedEncoding) {
response.setHeader('Content-Encoding', matchedEncoding)
}
}, content, file.doNotCache)
} else {
next()
}
request.resume()
})
}
}
createSourceFilesMiddleware.$inject = [
'filesPromise', 'serveFile', 'config.basePath', 'config.urlRoot'
]
exports.create = createSourceFilesMiddleware

18
node_modules/karma/lib/middleware/stopper.js generated vendored Normal file
View file

@ -0,0 +1,18 @@
/**
* Stopper middleware is responsible for communicating with `karma stop`.
*/
const log = require('../logger').create('middleware:stopper')
function createStopperMiddleware (urlRoot) {
return function (request, response, next) {
if (request.url !== urlRoot + 'stop') return next()
response.writeHead(200)
log.info('Stopping server')
response.end('OK')
process.kill(process.pid, 'SIGINT')
}
}
createStopperMiddleware.$inject = ['config.urlRoot']
exports.create = createStopperMiddleware

11
node_modules/karma/lib/middleware/strip_host.js generated vendored Normal file
View file

@ -0,0 +1,11 @@
/**
* Strip hostname from request path
* This to handle requests that uses (normally over proxies) an absoluteURI as request path
*/
function stripHostFromUrl (url) {
return url.replace(/^https?:\/\/[a-z.:\d-]+\//, '/')
}
// PUBLIC API
exports.stripHost = stripHostFromUrl

98
node_modules/karma/lib/plugin.js generated vendored Normal file
View file

@ -0,0 +1,98 @@
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const helper = require('./helper')
const log = require('./logger').create('plugin')
const IGNORED_PACKAGES = ['karma-cli', 'karma-runner.github.com']
function resolve (plugins, emitter) {
const modules = []
function requirePlugin (name) {
log.debug(`Loading plugin ${name}.`)
try {
modules.push(require(name))
} catch (e) {
if (e.code === 'MODULE_NOT_FOUND' && e.message.includes(name)) {
log.error(`Cannot find plugin "${name}".\n Did you forget to install it?\n npm install ${name} --save-dev`)
} else {
log.error(`Error during loading "${name}" plugin:\n ${e.message}`)
}
emitter.emit('load_error', 'plug_in', name)
}
}
plugins.forEach(function (plugin) {
if (helper.isString(plugin)) {
if (!plugin.includes('*')) {
requirePlugin(plugin)
return
}
const pluginDirectory = path.normalize(path.join(__dirname, '/../..'))
const regexp = new RegExp(`^${plugin.replace(/\*/g, '.*').replace(/\//g, '[/\\\\]')}`)
log.debug(`Loading ${plugin} from ${pluginDirectory}`)
fs.readdirSync(pluginDirectory)
.map((e) => {
const modulePath = path.join(pluginDirectory, e)
if (e[0] === '@') {
return fs.readdirSync(modulePath).map((e) => path.join(modulePath, e))
}
return modulePath
})
.reduce((a, x) => a.concat(x), [])
.map((modulePath) => path.relative(pluginDirectory, modulePath))
.filter((moduleName) => !IGNORED_PACKAGES.includes(moduleName) && regexp.test(moduleName))
.forEach((pluginName) => requirePlugin(path.join(pluginDirectory, pluginName)))
} else if (helper.isObject(plugin)) {
log.debug(`Loading inline plugin defining ${Object.keys(plugin).join(', ')}.`)
modules.push(plugin)
} else {
log.error(`Invalid plugin ${plugin}`)
emitter.emit('load_error', 'plug_in', plugin)
}
})
return modules
}
/**
Create a function to handle errors in plugin loading.
@param {Object} injector, the dict of dependency injection objects.
@return function closed over injector, which reports errors.
*/
function createInstantiatePlugin (injector) {
const emitter = injector.get('emitter')
// Cache to avoid report errors multiple times per plugin.
const pluginInstances = new Map()
return function instantiatePlugin (kind, name) {
if (pluginInstances.has(name)) {
return pluginInstances.get(name)
}
let p
try {
p = injector.get(`${kind}:${name}`)
if (!p) {
log.error(`Failed to instantiate ${kind} ${name}`)
emitter.emit('load_error', kind, name)
}
} catch (e) {
if (e.message.includes(`No provider for "${kind}:${name}"`)) {
log.error(`Cannot load "${name}", it is not registered!\n Perhaps you are missing some plugin?`)
} else {
log.error(`Cannot load "${name}"!\n ` + e.stack)
}
emitter.emit('load_error', kind, name)
}
pluginInstances.set(name, p, `${kind}:${name}`)
return p
}
}
createInstantiatePlugin.$inject = ['injector']
module.exports = { resolve, createInstantiatePlugin }

111
node_modules/karma/lib/preprocessor.js generated vendored Normal file
View file

@ -0,0 +1,111 @@
'use strict'
const util = require('util')
const fs = require('graceful-fs')
// bind need only for mock unit tests
const readFile = util.promisify(fs.readFile.bind(fs))
const tryToRead = function (path, log) {
const maxRetries = 3
let promise = readFile(path)
for (let retryCount = 1; retryCount <= maxRetries; retryCount++) {
promise = promise.catch((err) => {
log.warn(err)
log.warn('retrying ' + retryCount)
return readFile(path)
})
}
return promise.catch((err) => {
log.warn(err)
return Promise.reject(err)
})
}
const mm = require('minimatch')
const { isBinaryFile } = require('isbinaryfile')
const _ = require('lodash')
const CryptoUtils = require('./utils/crypto-utils')
const log = require('./logger').create('preprocess')
function executeProcessor (process, file, content) {
let done = null
const donePromise = new Promise((resolve, reject) => {
done = function (error, content) {
// normalize B-C
if (arguments.length === 1 && typeof error === 'string') {
content = error
error = null
}
if (error) {
reject(error)
} else {
resolve(content)
}
}
})
return (process(content, file, done) || Promise.resolve()).then((content) => {
if (content) {
// async process correctly returned content
return content
}
// process called done() (Either old sync api or an async function that did not return content)
return donePromise
})
}
async function runProcessors (preprocessors, file, content) {
try {
for (const process of preprocessors) {
content = await executeProcessor(process, file, content)
}
} catch (error) {
file.contentPath = null
file.content = null
throw error
}
file.contentPath = null
file.content = content
file.sha = CryptoUtils.sha1(content)
}
function createPriorityPreprocessor (config = {}, preprocessorPriority, basePath, instantiatePlugin) {
_.union.apply(_, Object.values(config)).forEach((name) => instantiatePlugin('preprocessor', name))
return async function preprocess (file) {
const buffer = await tryToRead(file.originalPath, log)
let isBinary = file.isBinary
if (isBinary == null) {
// Pattern did not specify, probe for it.
isBinary = await isBinaryFile(buffer, buffer.length)
}
const preprocessorNames = Object.keys(config).reduce((ppNames, pattern) => {
if (mm(file.originalPath, pattern, { dot: true })) {
ppNames = _.union(ppNames, config[pattern])
}
return ppNames
}, [])
// Apply preprocessor priority.
const preprocessors = preprocessorNames
.map((name) => [name, preprocessorPriority[name] || 0])
.sort((a, b) => b[1] - a[1])
.map((duo) => duo[0])
.reduce((preProcs, name) => {
const p = instantiatePlugin('preprocessor', name)
if (!isBinary || (p && p.handleBinaryFiles)) {
preProcs.push(p)
} else {
log.warn(`Ignored preprocessing ${file.originalPath} because ${name} has handleBinaryFiles=false.`)
}
return preProcs
}, [])
await runProcessors(preprocessors, file, isBinary ? buffer : buffer.toString())
}
}
createPriorityPreprocessor.$inject = ['config.preprocessors', 'config.preprocessor_priority', 'config.basePath', 'instantiatePlugin']
exports.createPriorityPreprocessor = createPriorityPreprocessor

152
node_modules/karma/lib/reporter.js generated vendored Normal file
View file

@ -0,0 +1,152 @@
'use strict'
// eslint-disable-next-line node/no-deprecated-api
const resolve = require('url').resolve
const SourceMapConsumer = require('source-map').SourceMapConsumer
const _ = require('lodash')
const PathUtils = require('./utils/path-utils')
const log = require('./logger').create('reporter')
const MultiReporter = require('./reporters/multi')
const baseReporterDecoratorFactory = require('./reporters/base').decoratorFactory
function createErrorFormatter (config, emitter, SourceMapConsumer) {
const basePath = config.basePath
const urlRoot = config.urlRoot === '/' ? '' : (config.urlRoot || '')
let lastServedFiles = []
emitter.on('file_list_modified', (files) => {
lastServedFiles = files.served
})
const URL_REGEXP = new RegExp('(?:https?:\\/\\/' +
config.hostname + '(?:\\:' + config.port + ')?' + ')?\\/?' +
urlRoot + '\\/?' +
'(base/|absolute)' + // prefix, including slash for base/ to create relative paths.
'((?:[A-z]\\:)?[^\\?\\s\\:]*)' + // path
'(\\?\\w*)?' + // sha
'(\\:(\\d+))?' + // line
'(\\:(\\d+))?' + // column
'', 'g')
const cache = new WeakMap()
function getSourceMapConsumer (sourceMap) {
if (!cache.has(sourceMap)) {
cache.set(sourceMap, new SourceMapConsumer(sourceMap))
}
return cache.get(sourceMap)
}
return function (input, indentation) {
indentation = _.isString(indentation) ? indentation : ''
if (_.isError(input)) {
input = input.message
} else if (_.isEmpty(input)) {
input = ''
} else if (!_.isString(input)) {
input = JSON.stringify(input, null, indentation)
}
let msg = input.replace(URL_REGEXP, function (stackTracePath, prefix, path, __, ___, line, ____, column) {
const normalizedPath = prefix === 'base/' ? `${basePath}/${path}` : path
const file = lastServedFiles.find((file) => file.path === normalizedPath)
if (file && file.sourceMap && line) {
line = +line
column = +column
// When no column is given and we default to 0, it doesn't make sense to only search for smaller
// or equal columns in the sourcemap, let's search for equal or greater columns.
const bias = column ? SourceMapConsumer.GREATEST_LOWER_BOUND : SourceMapConsumer.LEAST_UPPER_BOUND
try {
const zeroBasedColumn = Math.max(0, (column || 1) - 1)
const original = getSourceMapConsumer(file.sourceMap).originalPositionFor({ line, column: zeroBasedColumn, bias })
// If there is no original position/source for the current stack trace path, then
// we return early with the formatted generated position. This handles the case of
// generated code which does not map to anything, see Case 1 of the source-map spec.
// https://sourcemaps.info/spec.html.
if (original.source === null) {
return PathUtils.formatPathMapping(path, line, column)
}
// Source maps often only have a local file name, resolve to turn into a full path if
// the path is not absolute yet.
const oneBasedOriginalColumn = original.column == null ? original.column : original.column + 1
return `${PathUtils.formatPathMapping(resolve(path, original.source), original.line, oneBasedOriginalColumn)} <- ${PathUtils.formatPathMapping(path, line, column)}`
} catch (e) {
log.warn(`An unexpected error occurred while resolving the original position for: ${stackTracePath}`)
log.warn(e)
}
}
return PathUtils.formatPathMapping(path, line, column) || prefix
})
if (indentation) {
msg = indentation + msg.replace(/\n/g, '\n' + indentation)
}
return config.formatError ? config.formatError(msg) : msg + '\n'
}
}
function createReporters (names, config, emitter, injector) {
const errorFormatter = createErrorFormatter(config, emitter, SourceMapConsumer)
const reporters = []
names.forEach((name) => {
if (['dots', 'progress'].includes(name)) {
[
require(`./reporters/${name}`),
require(`./reporters/${name}_color`)
].forEach((Reporter) => {
reporters.push(new Reporter(errorFormatter, config.reportSlowerThan, config.colors, config.browserConsoleLogOptions))
})
return
}
const locals = {
baseReporterDecorator: ['factory', baseReporterDecoratorFactory],
formatError: ['value', errorFormatter]
}
try {
log.debug(`Trying to load reporter: ${name}`)
reporters.push(injector.createChild([locals], ['reporter:' + name]).get('reporter:' + name))
} catch (e) {
if (e.message.includes(`No provider for "reporter:${name}"`)) {
log.error(`Can not load reporter "${name}", it is not registered!\n Perhaps you are missing some plugin?`)
} else {
log.error(`Can not load "${name}"!\n ${e.stack}`)
}
emitter.emit('load_error', 'reporter', name)
return
}
const colorName = name + '_color'
if (!names.includes(colorName)) {
try {
log.debug(`Trying to load color-version of reporter: ${name} (${colorName})`)
reporters.push(injector.createChild([locals], ['reporter:' + colorName]).get('reporter:' + name))
} catch (e) {
log.debug('Couldn\'t load color-version.')
}
}
})
reporters.forEach((reporter) => emitter.bind(reporter))
return new MultiReporter(reporters)
}
createReporters.$inject = [
'config.reporters',
'config',
'emitter',
'injector'
]
exports.createReporters = createReporters

162
node_modules/karma/lib/reporters/base.js generated vendored Normal file
View file

@ -0,0 +1,162 @@
'use strict'
const util = require('util')
const constants = require('../constants')
const helper = require('../helper')
const BaseReporter = function (formatError, reportSlow, useColors, browserConsoleLogOptions, adapter) {
this.adapters = [adapter || process.stdout.write.bind(process.stdout)]
this.USE_COLORS = false
this.EXCLUSIVELY_USE_COLORS = undefined
this.LOG_SINGLE_BROWSER = '%s: %s\n'
this.LOG_MULTI_BROWSER = '%s %s: %s\n'
this.SPEC_FAILURE = '%s %s FAILED' + '\n'
this.SPEC_SLOW = '%s SLOW %s: %s\n'
this.ERROR = '%s ERROR\n'
this.FINISHED_ERROR = ' ERROR'
this.FINISHED_SUCCESS = ' SUCCESS'
this.FINISHED_DISCONNECTED = ' DISCONNECTED'
this.X_FAILED = ' (%d FAILED)'
this.TOTAL_SUCCESS = 'TOTAL: %d SUCCESS\n'
this.TOTAL_FAILED = 'TOTAL: %d FAILED, %d SUCCESS\n'
this.onRunStart = () => {
this._browsers = []
}
this.onBrowserStart = (browser) => {
this._browsers.push(browser)
}
this.renderBrowser = (browser) => {
const results = browser.lastResult
const totalExecuted = results.success + results.failed
let msg = `${browser}: Executed ${totalExecuted} of ${results.total}`
if (results.failed) {
msg += util.format(this.X_FAILED, results.failed)
}
if (results.skipped) {
msg += ` (skipped ${results.skipped})`
}
if (browser.isConnected) {
if (results.disconnected) {
msg += this.FINISHED_DISCONNECTED
} else if (results.error) {
msg += this.FINISHED_ERROR
} else if (!results.failed) {
msg += this.FINISHED_SUCCESS
}
msg += ` (${helper.formatTimeInterval(results.totalTime)} / ${helper.formatTimeInterval(results.netTime)})`
}
return msg
}
this.write = function () {
const msg = util.format.apply(null, Array.prototype.slice.call(arguments))
this.adapters.forEach((adapter) => {
if (!helper.isDefined(adapter.colors)) {
adapter.colors = useColors
}
if (!helper.isDefined(this.EXCLUSIVELY_USE_COLORS) || adapter.colors === this.EXCLUSIVELY_USE_COLORS) {
return adapter(msg)
}
})
}
this.writeCommonMsg = function () {
this.write.apply(this, arguments)
}
this.onBrowserError = (browser, error) => {
this.writeCommonMsg(util.format(this.ERROR, browser) + formatError(error, ' '))
}
this.onBrowserLog = (browser, log, type) => {
if (!browserConsoleLogOptions || !browserConsoleLogOptions.terminal) return
type = type.toUpperCase()
if (browserConsoleLogOptions.level) {
const logPriority = constants.LOG_PRIORITIES.indexOf(browserConsoleLogOptions.level.toUpperCase())
if (constants.LOG_PRIORITIES.indexOf(type) > logPriority) return
}
if (!helper.isString(log)) {
// TODO(vojta): change util to new syntax (config object)
log = util.inspect(log, false, undefined, this.USE_COLORS)
}
if (this._browsers && this._browsers.length === 1) {
this.writeCommonMsg(util.format(this.LOG_SINGLE_BROWSER, type, log))
} else {
this.writeCommonMsg(util.format(this.LOG_MULTI_BROWSER, browser, type, log))
}
}
this.onSpecComplete = (browser, result) => {
if (result.skipped) {
this.specSkipped(browser, result)
} else if (result.success) {
this.specSuccess(browser, result)
} else {
this.specFailure(browser, result)
}
if (reportSlow && result.time > reportSlow) {
const specName = result.suite.join(' ') + ' ' + result.description
const time = helper.formatTimeInterval(result.time)
this.writeCommonMsg(util.format(this.SPEC_SLOW, browser, time, specName))
}
}
this.specSuccess = () => {
}
this.specSkipped = () => {
}
this.specFailure = (browser, result) => {
const specName = result.suite.join(' ') + ' ' + result.description
let msg = util.format(this.SPEC_FAILURE, browser, specName)
result.log.forEach((log) => {
msg += formatError(log, '\t')
})
this.writeCommonMsg(msg)
}
this.onRunComplete = (browsers, results) => {
if (browsers.length >= 1 && !results.error && !results.disconnected) {
if (!results.failed) {
this.write(this.TOTAL_SUCCESS, results.success)
} else {
this.write(this.TOTAL_FAILED, results.failed, results.success)
}
}
}
}
BaseReporter.decoratorFactory = function (formatError, reportSlow, useColors, browserConsoleLogOptions) {
return function (self) {
BaseReporter.call(self, formatError, reportSlow, useColors, browserConsoleLogOptions)
}
}
BaseReporter.decoratorFactory.$inject = [
'formatError',
'config.reportSlowerThan',
'config.colors',
'config.browserConsoleLogOptions'
]
// PUBLISH
module.exports = BaseReporter

24
node_modules/karma/lib/reporters/base_color.js generated vendored Normal file
View file

@ -0,0 +1,24 @@
const { red, yellow, green, cyan } = require('@colors/colors/safe')
function BaseColorReporter () {
this.USE_COLORS = true
this.LOG_SINGLE_BROWSER = '%s: ' + cyan('%s') + '\n'
this.LOG_MULTI_BROWSER = '%s %s: ' + cyan('%s') + '\n'
this.SPEC_FAILURE = red('%s %s FAILED') + '\n'
this.SPEC_SLOW = yellow('%s SLOW %s: %s') + '\n'
this.ERROR = red('%s ERROR') + '\n'
this.FINISHED_ERROR = red(' ERROR')
this.FINISHED_SUCCESS = green(' SUCCESS')
this.FINISHED_DISCONNECTED = red(' DISCONNECTED')
this.X_FAILED = red(' (%d FAILED)')
this.TOTAL_SUCCESS = green('TOTAL: %d SUCCESS') + '\n'
this.TOTAL_FAILED = red('TOTAL: %d FAILED, %d SUCCESS') + '\n'
}
// PUBLISH
module.exports = BaseColorReporter

47
node_modules/karma/lib/reporters/dots.js generated vendored Normal file
View file

@ -0,0 +1,47 @@
const BaseReporter = require('./base')
function DotsReporter (formatError, reportSlow, useColors, browserConsoleLogOptions) {
BaseReporter.call(this, formatError, reportSlow, useColors, browserConsoleLogOptions)
const DOTS_WRAP = 80
this.EXCLUSIVELY_USE_COLORS = false
this.onRunStart = function () {
this._browsers = []
this._dotsCount = 0
}
this.onBrowserStart = function (browser) {
this._browsers.push(browser)
}
this.writeCommonMsg = function (msg) {
if (this._dotsCount) {
this._dotsCount = 0
msg = '\n' + msg
}
this.write(msg)
}
this.specSuccess = function () {
this._dotsCount = (this._dotsCount + 1) % DOTS_WRAP
this.write(this._dotsCount ? '.' : '.\n')
}
this.onBrowserComplete = function (browser) {
this.writeCommonMsg(this.renderBrowser(browser) + '\n')
}
this.onRunComplete = function (browsers, results) {
if (browsers.length > 1 && !results.disconnected && !results.error) {
if (!results.failed) {
this.write(this.TOTAL_SUCCESS, results.success)
} else {
this.write(this.TOTAL_FAILED, results.failed, results.success)
}
}
}
}
// PUBLISH
module.exports = DotsReporter

11
node_modules/karma/lib/reporters/dots_color.js generated vendored Normal file
View file

@ -0,0 +1,11 @@
const DotsReporter = require('./dots')
const BaseColorReporter = require('./base_color')
function DotsColorReporter (formatError, reportSlow, useColors, browserConsoleLogOptions) {
DotsReporter.call(this, formatError, reportSlow, useColors, browserConsoleLogOptions)
BaseColorReporter.call(this)
this.EXCLUSIVELY_USE_COLORS = true
}
// PUBLISH
module.exports = DotsColorReporter

19
node_modules/karma/lib/reporters/multi.js generated vendored Normal file
View file

@ -0,0 +1,19 @@
'use strict'
const helper = require('../helper')
class MultiReporter {
constructor (reporters) {
this._reporters = reporters
}
addAdapter (adapter) {
this._reporters.forEach((reporter) => reporter.adapters.push(adapter))
}
removeAdapter (adapter) {
this._reporters.forEach((reporter) => helper.arrayRemove(reporter.adapters, adapter))
}
}
module.exports = MultiReporter

63
node_modules/karma/lib/reporters/progress.js generated vendored Normal file
View file

@ -0,0 +1,63 @@
const BaseReporter = require('./base')
function ProgressReporter (formatError, reportSlow, useColors, browserConsoleLogOptions) {
BaseReporter.call(this, formatError, reportSlow, useColors, browserConsoleLogOptions)
this.EXCLUSIVELY_USE_COLORS = false
this._browsers = []
this.writeCommonMsg = function (msg) {
this.write(this._remove() + msg + this._render())
}
this.specSuccess = function () {
this.write(this._refresh())
}
this.onBrowserComplete = function () {
this.write(this._refresh())
}
this.onRunStart = function () {
this._browsers = []
this._isRendered = false
}
this.onBrowserStart = function (browser) {
this._browsers.push(browser)
if (this._isRendered) {
this.write('\n')
}
this.write(this._refresh())
}
this._remove = function () {
if (!this._isRendered) {
return ''
}
let cmd = ''
this._browsers.forEach(function () {
cmd += '\x1B[1A' + '\x1B[2K'
})
this._isRendered = false
return cmd
}
this._render = function () {
this._isRendered = true
return this._browsers.map(this.renderBrowser).join('\n') + '\n'
}
this._refresh = function () {
return this._remove() + this._render()
}
}
// PUBLISH
module.exports = ProgressReporter

11
node_modules/karma/lib/reporters/progress_color.js generated vendored Normal file
View file

@ -0,0 +1,11 @@
const ProgressReporter = require('./progress')
const BaseColorReporter = require('./base_color')
function ProgressColorReporter (formatError, reportSlow, useColors, browserConsoleLogOptions) {
ProgressReporter.call(this, formatError, reportSlow, useColors, browserConsoleLogOptions)
BaseColorReporter.call(this)
this.EXCLUSIVELY_USE_COLORS = true
}
// PUBLISH
module.exports = ProgressColorReporter

113
node_modules/karma/lib/runner.js generated vendored Normal file
View file

@ -0,0 +1,113 @@
'use strict'
const http = require('http')
const constant = require('./constants')
const EventEmitter = require('events').EventEmitter
const helper = require('./helper')
const cfg = require('./config')
const logger = require('./logger')
const { lookup } = require('./utils/dns-utils')
const log = logger.create('runner')
function parseExitCode (buffer, defaultExitCode, failOnEmptyTestSuite) {
const tailPos = buffer.length - Buffer.byteLength(constant.EXIT_CODE) - 2
if (tailPos < 0) {
return { exitCode: defaultExitCode, buffer }
}
const tail = buffer.slice(tailPos)
const tailStr = tail.toString()
if (tailStr.slice(0, -2) === constant.EXIT_CODE) {
const emptyInt = parseInt(tailStr.slice(-2, -1), 10)
let exitCode = parseInt(tailStr.slice(-1), 10)
if (failOnEmptyTestSuite === false && emptyInt === 0) {
log.warn('Test suite was empty.')
exitCode = 0
}
return { exitCode, buffer: buffer.slice(0, tailPos) }
}
return { exitCode: defaultExitCode, buffer }
}
// TODO(vojta): read config file (port, host, urlRoot)
function run (cliOptionsOrConfig, done) {
cliOptionsOrConfig = cliOptionsOrConfig || {}
done = helper.isFunction(done) ? done : process.exit
let config
if (cliOptionsOrConfig instanceof cfg.Config) {
config = cliOptionsOrConfig
} else {
logger.setupFromConfig({
colors: cliOptionsOrConfig.colors,
logLevel: cliOptionsOrConfig.logLevel
})
const deprecatedCliOptionsMessage =
'Passing raw CLI options to `runner(config, done)` is deprecated. Use ' +
'`parseConfig(configFilePath, cliOptions, {promiseConfig: true, throwErrors: true})` ' +
'to prepare a processed `Config` instance and pass that as the ' +
'`config` argument instead.'
log.warn(deprecatedCliOptionsMessage)
try {
config = cfg.parseConfig(
cliOptionsOrConfig.configFile,
cliOptionsOrConfig,
{
promiseConfig: false,
throwErrors: true
}
)
} catch (parseConfigError) {
// TODO: change how `done` falls back to exit in next major version
// SEE: https://github.com/karma-runner/karma/pull/3635#discussion_r565399378
done(1)
}
}
let exitCode = 1
const emitter = new EventEmitter()
const options = {
hostname: config.hostname,
path: config.urlRoot + 'run',
port: config.port,
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
lookup
}
const request = http.request(options, function (response) {
response.on('data', function (buffer) {
const parsedResult = parseExitCode(buffer, exitCode, config.failOnEmptyTestSuite)
exitCode = parsedResult.exitCode
emitter.emit('progress', parsedResult.buffer)
})
response.on('end', () => done(exitCode))
})
request.on('error', function (e) {
if (e.code === 'ECONNREFUSED') {
log.error('There is no server listening on port %d', options.port)
done(1, e.code)
} else {
throw e
}
})
request.end(JSON.stringify({
args: config.clientArgs,
removedFiles: config.removedFiles,
changedFiles: config.changedFiles,
addedFiles: config.addedFiles,
refresh: config.refresh,
colors: config.colors
}))
return emitter
}
exports.run = run

487
node_modules/karma/lib/server.js generated vendored Normal file
View file

@ -0,0 +1,487 @@
'use strict'
const SocketIO = require('socket.io')
const di = require('di')
const util = require('util')
const spawn = require('child_process').spawn
const tmp = require('tmp')
const fs = require('fs')
const path = require('path')
const NetUtils = require('./utils/net-utils')
const root = global || window || this
const cfg = require('./config')
const logger = require('./logger')
const constant = require('./constants')
const watcher = require('./watcher')
const plugin = require('./plugin')
const createServeFile = require('./web-server').createServeFile
const createServeStaticFile = require('./web-server').createServeStaticFile
const createFilesPromise = require('./web-server').createFilesPromise
const createWebServer = require('./web-server').createWebServer
const preprocessor = require('./preprocessor')
const Launcher = require('./launcher').Launcher
const FileList = require('./file-list')
const reporter = require('./reporter')
const helper = require('./helper')
const events = require('./events')
const KarmaEventEmitter = events.EventEmitter
const EventEmitter = require('events').EventEmitter
const Executor = require('./executor')
const Browser = require('./browser')
const BrowserCollection = require('./browser_collection')
const EmitterWrapper = require('./emitter_wrapper')
const processWrapper = new EmitterWrapper(process)
function createSocketIoServer (webServer, executor, config) {
const server = new SocketIO.Server(webServer, {
// avoid destroying http upgrades from socket.io to get proxied websockets working
destroyUpgrade: false,
path: config.urlRoot + 'socket.io/',
transports: config.transports,
forceJSONP: config.forceJSONP,
// Default is 5000 in socket.io v2.x and v3.x.
pingTimeout: config.pingTimeout || 5000,
// Default in v2 is 1e8 and coverage results can fail at 1e6
maxHttpBufferSize: 1e8
})
// hack to overcome circular dependency
executor.socketIoSockets = server.sockets
return server
}
class Server extends KarmaEventEmitter {
constructor (cliOptionsOrConfig, done) {
super()
cliOptionsOrConfig = cliOptionsOrConfig || {}
this.log = logger.create('karma-server')
done = helper.isFunction(done) ? done : process.exit
this.loadErrors = []
let config
if (cliOptionsOrConfig instanceof cfg.Config) {
config = cliOptionsOrConfig
} else {
logger.setupFromConfig({
colors: cliOptionsOrConfig.colors,
logLevel: cliOptionsOrConfig.logLevel
})
const deprecatedCliOptionsMessage =
'Passing raw CLI options to `new Server(config, done)` is ' +
'deprecated. Use ' +
'`parseConfig(configFilePath, cliOptions, {promiseConfig: true, throwErrors: true})` ' +
'to prepare a processed `Config` instance and pass that as the ' +
'`config` argument instead.'
this.log.warn(deprecatedCliOptionsMessage)
try {
config = cfg.parseConfig(
cliOptionsOrConfig.configFile,
cliOptionsOrConfig,
{
promiseConfig: false,
throwErrors: true
}
)
} catch (parseConfigError) {
// TODO: change how `done` falls back to exit in next major version
// SEE: https://github.com/karma-runner/karma/pull/3635#discussion_r565399378
done(1)
return
}
}
this.log.debug('Final config', util.inspect(config, false, /** depth **/ null))
if (!config.autoWatch && !config.singleRun) {
this.log.warn('`autowatch` and `singleRun` are both `false`. In order to execute tests use `karma run`.')
}
let modules = [{
helper: ['value', helper],
logger: ['value', logger],
done: ['value', done || process.exit],
emitter: ['value', this],
server: ['value', this],
watcher: ['value', watcher],
launcher: ['factory', Launcher.factory],
config: ['value', config],
instantiatePlugin: ['factory', plugin.createInstantiatePlugin],
preprocess: ['factory', preprocessor.createPriorityPreprocessor],
fileList: ['factory', FileList.factory],
webServer: ['factory', createWebServer],
serveFile: ['factory', createServeFile],
serveStaticFile: ['factory', createServeStaticFile],
filesPromise: ['factory', createFilesPromise],
socketServer: ['factory', createSocketIoServer],
executor: ['factory', Executor.factory],
// TODO: Deprecated. Remove in the next major
customFileHandlers: ['value', []],
reporter: ['factory', reporter.createReporters],
capturedBrowsers: ['factory', BrowserCollection.factory],
args: ['value', {}],
timer: ['value', {
setTimeout () {
return setTimeout.apply(root, arguments)
},
clearTimeout
}]
}]
this.on('load_error', (type, name) => {
this.log.debug(`Registered a load error of type ${type} with name ${name}`)
this.loadErrors.push([type, name])
})
modules = modules.concat(plugin.resolve(config.plugins, this))
this._injector = new di.Injector(modules)
}
async start () {
const config = this.get('config')
try {
this._boundServer = await NetUtils.bindAvailablePort(config.port, config.listenAddress)
this._boundServer.on('connection', (socket) => {
// Attach an error handler to avoid UncaughtException errors.
socket.on('error', (err) => {
// Errors on this socket are retried, ignore them
this.log.debug('Ignoring error on webserver connection: ' + err)
})
})
config.port = this._boundServer.address().port
await this._injector.invoke(this._start, this)
} catch (err) {
this.log.error(`Server start failed on port ${config.port}: ${err}`)
this._close(1)
}
}
get (token) {
return this._injector.get(token)
}
refreshFiles () {
return this._fileList ? this._fileList.refresh() : Promise.resolve()
}
refreshFile (path) {
return this._fileList ? this._fileList.changeFile(path) : Promise.resolve()
}
emitExitAsync (code) {
const name = 'exit'
let pending = this.listeners(name).length
const deferred = helper.defer()
function resolve () {
deferred.resolve(code)
}
try {
this.emit(name, (newCode) => {
if (newCode && typeof newCode === 'number') {
// Only update code if it is given and not zero
code = newCode
}
if (!--pending) {
resolve()
}
})
if (!pending) {
resolve()
}
} catch (err) {
deferred.reject(err)
}
return deferred.promise
}
async _start (config, launcher, preprocess, fileList, capturedBrowsers, executor, done) {
if (config.detached) {
this._detach(config, done)
return
}
this._fileList = fileList
await Promise.all(
config.frameworks.map((framework) => this._injector.get('framework:' + framework))
)
const webServer = this._injector.get('webServer')
const socketServer = this._injector.get('socketServer')
const singleRunDoneBrowsers = Object.create(null)
const singleRunBrowsers = new BrowserCollection(new EventEmitter())
let singleRunBrowserNotCaptured = false
webServer.on('error', (err) => {
this.log.error(`Webserver fail ${err}`)
this._close(1)
})
const afterPreprocess = () => {
if (config.autoWatch) {
const watcher = this.get('watcher')
this._injector.invoke(watcher)
}
webServer.listen(this._boundServer, () => {
this.log.info(`Karma v${constant.VERSION} server started at ${config.protocol}//${config.hostname}:${config.port}${config.urlRoot}`)
this.emit('listening', config.port)
if (config.browsers && config.browsers.length) {
this._injector.invoke(launcher.launch, launcher).forEach((browserLauncher) => {
singleRunDoneBrowsers[browserLauncher.id] = false
})
}
if (this.loadErrors.length > 0) {
this.log.error(new Error(`Found ${this.loadErrors.length} load error${this.loadErrors.length === 1 ? '' : 's'}`))
this._close(1)
}
})
}
fileList.refresh().then(afterPreprocess, (err) => {
this.log.error('Error during file loading or preprocessing\n' + err.stack || err)
afterPreprocess()
})
this.on('browsers_change', () => socketServer.sockets.emit('info', capturedBrowsers.serialize()))
this.on('browser_register', (browser) => {
launcher.markCaptured(browser.id)
if (launcher.areAllCaptured()) {
this.emit('browsers_ready')
if (config.autoWatch) {
executor.schedule()
}
}
})
if (config.browserConsoleLogOptions && config.browserConsoleLogOptions.path) {
const configLevel = config.browserConsoleLogOptions.level || 'debug'
const configFormat = config.browserConsoleLogOptions.format || '%b %T: %m'
const configPath = config.browserConsoleLogOptions.path
const configPathDir = path.dirname(configPath)
if (!fs.existsSync(configPathDir)) fs.mkdirSync(configPathDir, { recursive: true })
this.log.info(`Writing browser console to file: ${configPath}`)
const browserLogFile = fs.openSync(configPath, 'w+')
const levels = ['log', 'error', 'warn', 'info', 'debug']
this.on('browser_log', function (browser, message, level) {
if (levels.indexOf(level.toLowerCase()) > levels.indexOf(configLevel)) {
return
}
if (!helper.isString(message)) {
message = util.inspect(message, { showHidden: false, colors: false })
}
const logMap = { '%m': message, '%t': level.toLowerCase(), '%T': level.toUpperCase(), '%b': browser }
const logString = configFormat.replace(/%[mtTb]/g, (m) => logMap[m])
this.log.debug(`Writing browser console line: ${logString}`)
fs.writeSync(browserLogFile, logString + '\n')
})
}
socketServer.sockets.on('connection', (socket) => {
this.log.debug(`A browser has connected on socket ${socket.id}`)
const replySocketEvents = events.bufferEvents(socket, ['start', 'info', 'karma_error', 'result', 'complete'])
socket.on('error', (err) => {
this.log.debug('karma server socket error: ' + err)
})
socket.on('register', (info) => {
const knownBrowser = info.id ? (capturedBrowsers.getById(info.id) || singleRunBrowsers.getById(info.id)) : null
if (knownBrowser) {
knownBrowser.reconnect(socket, info.isSocketReconnect)
} else {
const newBrowser = this._injector.createChild([{
id: ['value', info.id || null],
fullName: ['value', (helper.isDefined(info.displayName) ? info.displayName : info.name)],
socket: ['value', socket]
}]).invoke(Browser.factory)
newBrowser.init()
if (config.singleRun) {
newBrowser.execute()
singleRunBrowsers.add(newBrowser)
}
}
replySocketEvents()
})
})
const emitRunCompleteIfAllBrowsersDone = () => {
if (Object.keys(singleRunDoneBrowsers).every((key) => singleRunDoneBrowsers[key])) {
this.emit('run_complete', singleRunBrowsers, singleRunBrowsers.getResults(singleRunBrowserNotCaptured, config))
}
}
this.on('browser_complete', (completedBrowser) => {
if (completedBrowser.lastResult.disconnected && completedBrowser.disconnectsCount <= config.browserDisconnectTolerance) {
this.log.info(`Restarting ${completedBrowser.name} (${completedBrowser.disconnectsCount} of ${config.browserDisconnectTolerance} attempts)`)
if (!launcher.restart(completedBrowser.id)) {
this.emit('browser_restart_failure', completedBrowser)
}
} else {
this.emit('browser_complete_with_no_more_retries', completedBrowser)
}
})
this.on('stop', (done) => {
this.log.debug('Received stop event, exiting.')
this._close()
done()
})
if (config.singleRun) {
this.on('browser_restart_failure', (completedBrowser) => {
singleRunDoneBrowsers[completedBrowser.id] = true
emitRunCompleteIfAllBrowsersDone()
})
// This is the normal exit trigger.
this.on('browser_complete_with_no_more_retries', function (completedBrowser) {
singleRunDoneBrowsers[completedBrowser.id] = true
if (launcher.kill(completedBrowser.id)) {
completedBrowser.remove()
}
emitRunCompleteIfAllBrowsersDone()
})
this.on('browser_process_failure', (browserLauncher) => {
singleRunDoneBrowsers[browserLauncher.id] = true
singleRunBrowserNotCaptured = true
emitRunCompleteIfAllBrowsersDone()
})
this.on('run_complete', (browsers, results) => {
this.log.debug('Run complete, exiting.')
this._close(results.exitCode)
})
this.emit('run_start', singleRunBrowsers)
}
if (config.autoWatch) {
this.on('file_list_modified', () => {
this.log.debug('List of files has changed, trying to execute')
if (config.restartOnFileChange) {
socketServer.sockets.emit('stop')
}
executor.schedule()
})
}
processWrapper.on('SIGINT', () => this._close())
processWrapper.on('SIGTERM', () => this._close())
const reportError = (error) => {
this.log.error(error)
process.emit('infrastructure_error', error)
this._close(1)
}
processWrapper.on('unhandledRejection', (error) => {
this.log.error(`UnhandledRejection: ${error.stack || error.message || String(error)}`)
reportError(error)
})
processWrapper.on('uncaughtException', (error) => {
this.log.error(`UncaughtException: ${error.stack || error.message || String(error)}`)
reportError(error)
})
}
_detach (config, done) {
const tmpFile = tmp.fileSync({ keep: true })
this.log.info('Starting karma detached')
this.log.info('Run "karma stop" to stop the server.')
this.log.debug(`Writing config to tmp-file ${tmpFile.name}`)
config.detached = false
try {
fs.writeFileSync(tmpFile.name, JSON.stringify(config), 'utf8')
} catch (e) {
this.log.error("Couldn't write temporary configuration file")
done(1)
return
}
const child = spawn(process.argv[0], [path.resolve(__dirname, '../lib/detached.js'), tmpFile.name], {
detached: true,
stdio: 'ignore'
})
child.unref()
}
/**
* Cleanup all resources allocated by Karma and call the `done` callback
* with the result of the tests execution.
*
* @param [exitCode] - Optional exit code. If omitted will be computed by
* 'exit' event listeners.
*/
_close (exitCode) {
const webServer = this._injector.get('webServer')
const socketServer = this._injector.get('socketServer')
const done = this._injector.get('done')
const webServerCloseTimeout = 3000
const sockets = socketServer.sockets.sockets
Object.keys(sockets).forEach((id) => {
const socket = sockets[id]
socket.removeAllListeners('disconnect')
if (!socket.disconnected) {
process.nextTick(socket.disconnect.bind(socket))
}
})
this.emitExitAsync(exitCode).catch((err) => {
this.log.error('Error while calling exit event listeners\n' + err.stack || err)
return 1
}).then((code) => {
socketServer.sockets.removeAllListeners()
socketServer.close()
let removeAllListenersDone = false
const removeAllListeners = () => {
if (removeAllListenersDone) {
return
}
removeAllListenersDone = true
webServer.removeAllListeners()
processWrapper.removeAllListeners()
done(code || 0)
}
const closeTimeout = setTimeout(removeAllListeners, webServerCloseTimeout)
webServer.close(() => {
clearTimeout(closeTimeout)
removeAllListeners()
})
})
}
stop () {
return this.emitAsync('stop')
}
}
Server.prototype._start.$inject = ['config', 'launcher', 'preprocess', 'fileList', 'capturedBrowsers', 'executor', 'done']
module.exports = Server

69
node_modules/karma/lib/stopper.js generated vendored Normal file
View file

@ -0,0 +1,69 @@
const http = require('http')
const cfg = require('./config')
const logger = require('./logger')
const helper = require('./helper')
const { lookup } = require('./utils/dns-utils')
exports.stop = function (cliOptionsOrConfig, done) {
cliOptionsOrConfig = cliOptionsOrConfig || {}
const log = logger.create('stopper')
done = helper.isFunction(done) ? done : process.exit
let config
if (cliOptionsOrConfig instanceof cfg.Config) {
config = cliOptionsOrConfig
} else {
logger.setupFromConfig({
colors: cliOptionsOrConfig.colors,
logLevel: cliOptionsOrConfig.logLevel
})
const deprecatedCliOptionsMessage =
'Passing raw CLI options to `stopper(config, done)` is deprecated. Use ' +
'`parseConfig(configFilePath, cliOptions, {promiseConfig: true, throwErrors: true})` ' +
'to prepare a processed `Config` instance and pass that as the ' +
'`config` argument instead.'
log.warn(deprecatedCliOptionsMessage)
try {
config = cfg.parseConfig(
cliOptionsOrConfig.configFile,
cliOptionsOrConfig,
{
promiseConfig: false,
throwErrors: true
}
)
} catch (parseConfigError) {
// TODO: change how `done` falls back to exit in next major version
// SEE: https://github.com/karma-runner/karma/pull/3635#discussion_r565399378
done(1)
}
}
const request = http.request({
hostname: config.hostname,
path: config.urlRoot + 'stop',
port: config.port,
method: 'GET',
lookup
})
request.on('response', function (response) {
if (response.statusCode === 200) {
log.info('Server stopped.')
done(0)
} else {
log.error(`Server returned status code: ${response.statusCode}`)
done(1)
}
})
request.on('error', function (e) {
if (e.code === 'ECONNREFUSED') {
log.error(`There is no server listening on port ${config.port}`)
done(1, e.code)
} else {
throw e
}
})
request.end()
}

31
node_modules/karma/lib/temp_dir.js generated vendored Normal file
View file

@ -0,0 +1,31 @@
'use strict'
const path = require('path')
const fs = require('graceful-fs')
const rimraf = require('rimraf')
const log = require('./logger').create('temp-dir')
const TEMP_DIR = require('os').tmpdir()
module.exports = {
getPath (suffix) {
return path.normalize(TEMP_DIR + suffix)
},
create (path) {
log.debug(`Creating temp dir at ${path}`)
try {
fs.mkdirSync(path)
} catch (e) {
log.warn(`Failed to create a temp dir at ${path}`)
}
return path
},
remove (path, done) {
log.debug(`Cleaning temp dir ${path}`)
rimraf(path, done)
}
}

31
node_modules/karma/lib/url.js generated vendored Normal file
View file

@ -0,0 +1,31 @@
'use strict'
const path = require('path')
const { URL } = require('url')
/**
* Url object used for tracking files in `file-list.js`.
*/
class Url {
constructor (path, type, integrity) {
this.path = path
this.originalPath = path
this.type = type
this.integrity = integrity
this.isUrl = true
}
/**
* Detect type from the file extension in the path part of the URL.
* @returns {string} detected file type or empty string
*/
detectType () {
return path.extname(new URL(this.path).pathname).slice(1)
}
toString () {
return this.path
}
}
module.exports = Url

14
node_modules/karma/lib/utils/crypto-utils.js generated vendored Normal file
View file

@ -0,0 +1,14 @@
'use strict'
const crypto = require('crypto')
const CryptoUtils = {
sha1 (data) {
return crypto
.createHash('sha1')
.update(data)
.digest('hex')
}
}
module.exports = CryptoUtils

11
node_modules/karma/lib/utils/dns-utils.js generated vendored Normal file
View file

@ -0,0 +1,11 @@
const dns = require('dns')
// Node >=17 has different DNS resolution (see
// https://github.com/nodejs/node/issues/40702), it resolves domains
// according to the OS settings instead of IPv4-address first. The Karma server
// only listens on IPv4 address (127.0.0.1) by default, but the requests are
// sent to `localhost` in several places and `localhost` is resolved into IPv6
// address (`::`). So the run/stop/proxy request is unable to reach the Karma
// server and produces an error. To mitigate this issue karma force the
// IPv4-address first approach in Node >=17 as well.
module.exports.lookup = (hostname, options, callback) => dns.lookup(hostname, { ...options, verbatim: false }, callback)

25
node_modules/karma/lib/utils/file-utils.js generated vendored Normal file
View file

@ -0,0 +1,25 @@
'use strict'
const fs = require('graceful-fs')
const FileUtils = {
readFile (path) {
return fs.readFileSync(path).toString()
},
saveFile (path, content) {
fs.writeFileSync(path, content)
},
copyFile (src, dest) {
FileUtils.saveFile(dest, FileUtils.readFile(src))
},
removeFileIfExists (src) {
if (fs.existsSync(src)) {
fs.unlinkSync(src)
}
}
}
module.exports = FileUtils

27
node_modules/karma/lib/utils/net-utils.js generated vendored Normal file
View file

@ -0,0 +1,27 @@
'use strict'
const net = require('net')
const NetUtils = {
bindAvailablePort (port, listenAddress) {
return new Promise((resolve, reject) => {
const server = net.createServer()
server
.on('error', (err) => {
server.close()
if (err.code === 'EADDRINUSE' || err.code === 'EACCES') {
server.listen(++port, listenAddress)
} else {
reject(new Error(`Failed to bind ${port}: ` + (err.stack || err)))
}
})
.on('listening', () => {
resolve(server)
})
.listen(port, listenAddress)
})
}
}
module.exports = NetUtils

16
node_modules/karma/lib/utils/path-utils.js generated vendored Normal file
View file

@ -0,0 +1,16 @@
'use strict'
const path = require('path')
const PathUtils = {
formatPathMapping (path, line, column) {
return path + (line ? `:${line}` : '') + (column ? `:${column}` : '')
},
calculateAbsolutePath (karmaRelativePath) {
return path.join(__dirname, '..', '..', karmaRelativePath)
}
}
module.exports = PathUtils

14
node_modules/karma/lib/utils/pattern-utils.js generated vendored Normal file
View file

@ -0,0 +1,14 @@
'use strict'
const path = require('path')
const PatternUtils = {
getBaseDir (pattern) {
return pattern
.replace(/[/\\][^/\\]*\*.*$/, '') // remove parts with *
.replace(/[/\\][^/\\]*[!+]\(.*$/, '') // remove parts with !(...) and +(...)
.replace(/[/\\][^/\\]*\)\?.*$/, '') || path.sep // remove parts with (...)?
}
}
module.exports = PatternUtils

85
node_modules/karma/lib/watcher.js generated vendored Normal file
View file

@ -0,0 +1,85 @@
'use strict'
const mm = require('minimatch')
const braces = require('braces')
const PatternUtils = require('./utils/pattern-utils')
const helper = require('./helper')
const log = require('./logger').create('watcher')
const DIR_SEP = require('path').sep
function watchPatterns (patterns, watcher) {
let expandedPatterns = []
patterns.map((pattern) => {
// expand ['a/{b,c}'] to ['a/b', 'a/c']
expandedPatterns = expandedPatterns.concat(braces.expand(pattern, { keepEscaping: true }))
})
expandedPatterns
.map(PatternUtils.getBaseDir)
.filter((path, index, paths) => paths.indexOf(path) === index) // filter unique values
.forEach((path, index, paths) => {
if (!paths.some((p) => path.startsWith(p + DIR_SEP))) {
watcher.add(path)
log.debug(`Watching "${path}"`)
}
})
}
function checkAnyPathMatch (patterns, path) {
return patterns.some((pattern) => mm(path, pattern, { dot: true }))
}
function createIgnore (patterns, excludes) {
return function (path, stat) {
if (stat && !stat.isDirectory()) {
return !checkAnyPathMatch(patterns, path) || checkAnyPathMatch(excludes, path)
} else {
return false
}
}
}
function getWatchedPatterns (patterns) {
return patterns
.filter((pattern) => pattern.watched)
.map((pattern) => pattern.pattern)
}
function watch (patterns, excludes, fileList, usePolling, emitter) {
const watchedPatterns = getWatchedPatterns(patterns)
// Lazy-load 'chokidar' to make the dependency optional. This is desired when
// third-party watchers are in use.
const chokidar = require('chokidar')
const watcher = new chokidar.FSWatcher({
usePolling: usePolling,
ignorePermissionErrors: true,
ignoreInitial: true,
ignored: createIgnore(watchedPatterns, excludes)
})
watchPatterns(watchedPatterns, watcher)
watcher
.on('add', (path) => fileList.addFile(helper.normalizeWinPath(path)))
.on('change', (path) => fileList.changeFile(helper.normalizeWinPath(path)))
.on('unlink', (path) => fileList.removeFile(helper.normalizeWinPath(path)))
.on('error', log.debug.bind(log))
emitter.on('exit', (done) => {
watcher.close()
done()
})
return watcher
}
watch.$inject = [
'config.files',
'config.exclude',
'fileList',
'config.usePolling',
'emitter'
]
module.exports = watch

118
node_modules/karma/lib/web-server.js generated vendored Normal file
View file

@ -0,0 +1,118 @@
'use strict'
const fs = require('graceful-fs')
const http = require('http')
const https = require('https')
const path = require('path')
const connect = require('connect')
const mimeType = require('mime')
const common = require('./middleware/common')
const runnerMiddleware = require('./middleware/runner')
const stopperMiddleware = require('./middleware/stopper')
const karmaMiddleware = require('./middleware/karma')
const sourceFilesMiddleware = require('./middleware/source_files')
const proxyMiddleware = require('./middleware/proxy')
const log = require('./logger').create('web-server')
function createCustomHandler (customFileHandlers, config) {
let warningDone = false
return function (request, response, next) {
const handler = customFileHandlers.find((handler) => handler.urlRegex.test(request.url))
if (customFileHandlers.length > 0 && !warningDone) {
warningDone = true
log.warn('The `customFileHandlers` is deprecated and will be removed in Karma 7. Please upgrade plugins relying on this provider.')
}
return handler
? handler.handler(request, response, 'fake/static', 'fake/adapter', config.basePath, 'fake/root')
: next()
}
}
createCustomHandler.$inject = ['customFileHandlers', 'config']
function createFilesPromise (emitter, fileList) {
// Set an empty list of files to avoid race issues with
// file_list_modified not having been emitted yet
let files = fileList.files
emitter.on('file_list_modified', (filesParam) => { files = filesParam })
return {
then (...args) {
return Promise.resolve(files).then(...args)
}
}
}
function createServeStaticFile (config) {
return common.createServeFile(fs, path.normalize(path.join(__dirname, '/../static')), config)
}
createServeStaticFile.$inject = ['config']
function createServeFile (config) {
return common.createServeFile(fs, null, config)
}
createServeFile.$inject = ['config']
function createWebServer (injector, config) {
const { mime = {} } = config
mimeType.define({ ...mime }, true)
const proxyMiddlewareInstance = injector.invoke(proxyMiddleware.create)
log.debug('Instantiating middleware')
const handler = connect()
if (config.beforeMiddleware) {
config.beforeMiddleware.forEach((middleware) => handler.use(injector.get('middleware:' + middleware)))
}
handler.use(injector.invoke(runnerMiddleware.create))
handler.use(injector.invoke(stopperMiddleware.create))
handler.use(injector.invoke(karmaMiddleware.create))
handler.use(injector.invoke(sourceFilesMiddleware.create))
// TODO(vojta): extract the proxy into a plugin
handler.use(proxyMiddlewareInstance)
// TODO: Deprecated. Remove in the next major
handler.use(injector.invoke(createCustomHandler))
if (config.middleware) {
config.middleware.forEach((middleware) => handler.use(injector.get('middleware:' + middleware)))
}
handler.use((request, response) => common.serve404(response, request.url))
let serverClass = http
const serverArguments = [handler]
if (config.protocol === 'https:') {
serverClass = https
serverArguments.unshift(config.httpsServerOptions || {})
}
if (config.httpModule) {
serverClass = config.httpModule
}
const server = serverClass.createServer.apply(null, serverArguments)
server.on('upgrade', function (req, socket, head) {
log.debug(`upgrade ${req.url}`)
proxyMiddlewareInstance.upgrade(req, socket, head)
})
return server
}
createWebServer.$inject = ['injector', 'config']
module.exports = {
createWebServer,
createServeFile,
createServeStaticFile,
createFilesPromise
}

13
node_modules/karma/local.log generated vendored Normal file
View file

@ -0,0 +1,13 @@
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Mon Jul 29 2024 17:37:36:600 GMT+0000 (UTC) -- Starting configuration console on http://localhost:45454
Mon Jul 29 2024 17:37:36:880 GMT+0000 (UTC) -- [INFO] Started the BrowserStack Binary server on 45691, PID: 5753
Mon Jul 29 2024 17:37:36:981 GMT+0000 (UTC) -- [SUCCESS] You can now access your local server(s) in our remote browser
Mon Jul 29 2024 17:37:37:555 GMT+0000 (UTC) -- Press Ctrl-C to exit

345
node_modules/karma/node_modules/ansi-styles/index.d.ts generated vendored Normal file
View file

@ -0,0 +1,345 @@
declare type CSSColor =
| 'aliceblue'
| 'antiquewhite'
| 'aqua'
| 'aquamarine'
| 'azure'
| 'beige'
| 'bisque'
| 'black'
| 'blanchedalmond'
| 'blue'
| 'blueviolet'
| 'brown'
| 'burlywood'
| 'cadetblue'
| 'chartreuse'
| 'chocolate'
| 'coral'
| 'cornflowerblue'
| 'cornsilk'
| 'crimson'
| 'cyan'
| 'darkblue'
| 'darkcyan'
| 'darkgoldenrod'
| 'darkgray'
| 'darkgreen'
| 'darkgrey'
| 'darkkhaki'
| 'darkmagenta'
| 'darkolivegreen'
| 'darkorange'
| 'darkorchid'
| 'darkred'
| 'darksalmon'
| 'darkseagreen'
| 'darkslateblue'
| 'darkslategray'
| 'darkslategrey'
| 'darkturquoise'
| 'darkviolet'
| 'deeppink'
| 'deepskyblue'
| 'dimgray'
| 'dimgrey'
| 'dodgerblue'
| 'firebrick'
| 'floralwhite'
| 'forestgreen'
| 'fuchsia'
| 'gainsboro'
| 'ghostwhite'
| 'gold'
| 'goldenrod'
| 'gray'
| 'green'
| 'greenyellow'
| 'grey'
| 'honeydew'
| 'hotpink'
| 'indianred'
| 'indigo'
| 'ivory'
| 'khaki'
| 'lavender'
| 'lavenderblush'
| 'lawngreen'
| 'lemonchiffon'
| 'lightblue'
| 'lightcoral'
| 'lightcyan'
| 'lightgoldenrodyellow'
| 'lightgray'
| 'lightgreen'
| 'lightgrey'
| 'lightpink'
| 'lightsalmon'
| 'lightseagreen'
| 'lightskyblue'
| 'lightslategray'
| 'lightslategrey'
| 'lightsteelblue'
| 'lightyellow'
| 'lime'
| 'limegreen'
| 'linen'
| 'magenta'
| 'maroon'
| 'mediumaquamarine'
| 'mediumblue'
| 'mediumorchid'
| 'mediumpurple'
| 'mediumseagreen'
| 'mediumslateblue'
| 'mediumspringgreen'
| 'mediumturquoise'
| 'mediumvioletred'
| 'midnightblue'
| 'mintcream'
| 'mistyrose'
| 'moccasin'
| 'navajowhite'
| 'navy'
| 'oldlace'
| 'olive'
| 'olivedrab'
| 'orange'
| 'orangered'
| 'orchid'
| 'palegoldenrod'
| 'palegreen'
| 'paleturquoise'
| 'palevioletred'
| 'papayawhip'
| 'peachpuff'
| 'peru'
| 'pink'
| 'plum'
| 'powderblue'
| 'purple'
| 'rebeccapurple'
| 'red'
| 'rosybrown'
| 'royalblue'
| 'saddlebrown'
| 'salmon'
| 'sandybrown'
| 'seagreen'
| 'seashell'
| 'sienna'
| 'silver'
| 'skyblue'
| 'slateblue'
| 'slategray'
| 'slategrey'
| 'snow'
| 'springgreen'
| 'steelblue'
| 'tan'
| 'teal'
| 'thistle'
| 'tomato'
| 'turquoise'
| 'violet'
| 'wheat'
| 'white'
| 'whitesmoke'
| 'yellow'
| 'yellowgreen';
declare namespace ansiStyles {
interface ColorConvert {
/**
The RGB color space.
@param red - (`0`-`255`)
@param green - (`0`-`255`)
@param blue - (`0`-`255`)
*/
rgb(red: number, green: number, blue: number): string;
/**
The RGB HEX color space.
@param hex - A hexadecimal string containing RGB data.
*/
hex(hex: string): string;
/**
@param keyword - A CSS color name.
*/
keyword(keyword: CSSColor): string;
/**
The HSL color space.
@param hue - (`0`-`360`)
@param saturation - (`0`-`100`)
@param lightness - (`0`-`100`)
*/
hsl(hue: number, saturation: number, lightness: number): string;
/**
The HSV color space.
@param hue - (`0`-`360`)
@param saturation - (`0`-`100`)
@param value - (`0`-`100`)
*/
hsv(hue: number, saturation: number, value: number): string;
/**
The HSV color space.
@param hue - (`0`-`360`)
@param whiteness - (`0`-`100`)
@param blackness - (`0`-`100`)
*/
hwb(hue: number, whiteness: number, blackness: number): string;
/**
Use a [4-bit unsigned number](https://en.wikipedia.org/wiki/ANSI_escape_code#3/4-bit) to set text color.
*/
ansi(ansi: number): string;
/**
Use an [8-bit unsigned number](https://en.wikipedia.org/wiki/ANSI_escape_code#8-bit) to set text color.
*/
ansi256(ansi: number): string;
}
interface CSPair {
/**
The ANSI terminal control sequence for starting this style.
*/
readonly open: string;
/**
The ANSI terminal control sequence for ending this style.
*/
readonly close: string;
}
interface ColorBase {
readonly ansi: ColorConvert;
readonly ansi256: ColorConvert;
readonly ansi16m: ColorConvert;
/**
The ANSI terminal control sequence for ending this color.
*/
readonly close: string;
}
interface Modifier {
/**
Resets the current color chain.
*/
readonly reset: CSPair;
/**
Make text bold.
*/
readonly bold: CSPair;
/**
Emitting only a small amount of light.
*/
readonly dim: CSPair;
/**
Make text italic. (Not widely supported)
*/
readonly italic: CSPair;
/**
Make text underline. (Not widely supported)
*/
readonly underline: CSPair;
/**
Inverse background and foreground colors.
*/
readonly inverse: CSPair;
/**
Prints the text, but makes it invisible.
*/
readonly hidden: CSPair;
/**
Puts a horizontal line through the center of the text. (Not widely supported)
*/
readonly strikethrough: CSPair;
}
interface ForegroundColor {
readonly black: CSPair;
readonly red: CSPair;
readonly green: CSPair;
readonly yellow: CSPair;
readonly blue: CSPair;
readonly cyan: CSPair;
readonly magenta: CSPair;
readonly white: CSPair;
/**
Alias for `blackBright`.
*/
readonly gray: CSPair;
/**
Alias for `blackBright`.
*/
readonly grey: CSPair;
readonly blackBright: CSPair;
readonly redBright: CSPair;
readonly greenBright: CSPair;
readonly yellowBright: CSPair;
readonly blueBright: CSPair;
readonly cyanBright: CSPair;
readonly magentaBright: CSPair;
readonly whiteBright: CSPair;
}
interface BackgroundColor {
readonly bgBlack: CSPair;
readonly bgRed: CSPair;
readonly bgGreen: CSPair;
readonly bgYellow: CSPair;
readonly bgBlue: CSPair;
readonly bgCyan: CSPair;
readonly bgMagenta: CSPair;
readonly bgWhite: CSPair;
/**
Alias for `bgBlackBright`.
*/
readonly bgGray: CSPair;
/**
Alias for `bgBlackBright`.
*/
readonly bgGrey: CSPair;
readonly bgBlackBright: CSPair;
readonly bgRedBright: CSPair;
readonly bgGreenBright: CSPair;
readonly bgYellowBright: CSPair;
readonly bgBlueBright: CSPair;
readonly bgCyanBright: CSPair;
readonly bgMagentaBright: CSPair;
readonly bgWhiteBright: CSPair;
}
}
declare const ansiStyles: {
readonly modifier: ansiStyles.Modifier;
readonly color: ansiStyles.ForegroundColor & ansiStyles.ColorBase;
readonly bgColor: ansiStyles.BackgroundColor & ansiStyles.ColorBase;
readonly codes: ReadonlyMap<number, number>;
} & ansiStyles.BackgroundColor & ansiStyles.ForegroundColor & ansiStyles.Modifier;
export = ansiStyles;

163
node_modules/karma/node_modules/ansi-styles/index.js generated vendored Normal file
View file

@ -0,0 +1,163 @@
'use strict';
const wrapAnsi16 = (fn, offset) => (...args) => {
const code = fn(...args);
return `\u001B[${code + offset}m`;
};
const wrapAnsi256 = (fn, offset) => (...args) => {
const code = fn(...args);
return `\u001B[${38 + offset};5;${code}m`;
};
const wrapAnsi16m = (fn, offset) => (...args) => {
const rgb = fn(...args);
return `\u001B[${38 + offset};2;${rgb[0]};${rgb[1]};${rgb[2]}m`;
};
const ansi2ansi = n => n;
const rgb2rgb = (r, g, b) => [r, g, b];
const setLazyProperty = (object, property, get) => {
Object.defineProperty(object, property, {
get: () => {
const value = get();
Object.defineProperty(object, property, {
value,
enumerable: true,
configurable: true
});
return value;
},
enumerable: true,
configurable: true
});
};
/** @type {typeof import('color-convert')} */
let colorConvert;
const makeDynamicStyles = (wrap, targetSpace, identity, isBackground) => {
if (colorConvert === undefined) {
colorConvert = require('color-convert');
}
const offset = isBackground ? 10 : 0;
const styles = {};
for (const [sourceSpace, suite] of Object.entries(colorConvert)) {
const name = sourceSpace === 'ansi16' ? 'ansi' : sourceSpace;
if (sourceSpace === targetSpace) {
styles[name] = wrap(identity, offset);
} else if (typeof suite === 'object') {
styles[name] = wrap(suite[targetSpace], offset);
}
}
return styles;
};
function assembleStyles() {
const codes = new Map();
const styles = {
modifier: {
reset: [0, 0],
// 21 isn't widely supported and 22 does the same thing
bold: [1, 22],
dim: [2, 22],
italic: [3, 23],
underline: [4, 24],
inverse: [7, 27],
hidden: [8, 28],
strikethrough: [9, 29]
},
color: {
black: [30, 39],
red: [31, 39],
green: [32, 39],
yellow: [33, 39],
blue: [34, 39],
magenta: [35, 39],
cyan: [36, 39],
white: [37, 39],
// Bright color
blackBright: [90, 39],
redBright: [91, 39],
greenBright: [92, 39],
yellowBright: [93, 39],
blueBright: [94, 39],
magentaBright: [95, 39],
cyanBright: [96, 39],
whiteBright: [97, 39]
},
bgColor: {
bgBlack: [40, 49],
bgRed: [41, 49],
bgGreen: [42, 49],
bgYellow: [43, 49],
bgBlue: [44, 49],
bgMagenta: [45, 49],
bgCyan: [46, 49],
bgWhite: [47, 49],
// Bright color
bgBlackBright: [100, 49],
bgRedBright: [101, 49],
bgGreenBright: [102, 49],
bgYellowBright: [103, 49],
bgBlueBright: [104, 49],
bgMagentaBright: [105, 49],
bgCyanBright: [106, 49],
bgWhiteBright: [107, 49]
}
};
// Alias bright black as gray (and grey)
styles.color.gray = styles.color.blackBright;
styles.bgColor.bgGray = styles.bgColor.bgBlackBright;
styles.color.grey = styles.color.blackBright;
styles.bgColor.bgGrey = styles.bgColor.bgBlackBright;
for (const [groupName, group] of Object.entries(styles)) {
for (const [styleName, style] of Object.entries(group)) {
styles[styleName] = {
open: `\u001B[${style[0]}m`,
close: `\u001B[${style[1]}m`
};
group[styleName] = styles[styleName];
codes.set(style[0], style[1]);
}
Object.defineProperty(styles, groupName, {
value: group,
enumerable: false
});
}
Object.defineProperty(styles, 'codes', {
value: codes,
enumerable: false
});
styles.color.close = '\u001B[39m';
styles.bgColor.close = '\u001B[49m';
setLazyProperty(styles.color, 'ansi', () => makeDynamicStyles(wrapAnsi16, 'ansi16', ansi2ansi, false));
setLazyProperty(styles.color, 'ansi256', () => makeDynamicStyles(wrapAnsi256, 'ansi256', ansi2ansi, false));
setLazyProperty(styles.color, 'ansi16m', () => makeDynamicStyles(wrapAnsi16m, 'rgb', rgb2rgb, false));
setLazyProperty(styles.bgColor, 'ansi', () => makeDynamicStyles(wrapAnsi16, 'ansi16', ansi2ansi, true));
setLazyProperty(styles.bgColor, 'ansi256', () => makeDynamicStyles(wrapAnsi256, 'ansi256', ansi2ansi, true));
setLazyProperty(styles.bgColor, 'ansi16m', () => makeDynamicStyles(wrapAnsi16m, 'rgb', rgb2rgb, true));
return styles;
}
// Make the export immutable
Object.defineProperty(module, 'exports', {
enumerable: true,
get: assembleStyles
});

9
node_modules/karma/node_modules/ansi-styles/license generated vendored Normal file
View file

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

View file

@ -0,0 +1,56 @@
{
"name": "ansi-styles",
"version": "4.3.0",
"description": "ANSI escape codes for styling strings in the terminal",
"license": "MIT",
"repository": "chalk/ansi-styles",
"funding": "https://github.com/chalk/ansi-styles?sponsor=1",
"author": {
"name": "Sindre Sorhus",
"email": "sindresorhus@gmail.com",
"url": "sindresorhus.com"
},
"engines": {
"node": ">=8"
},
"scripts": {
"test": "xo && ava && tsd",
"screenshot": "svg-term --command='node screenshot' --out=screenshot.svg --padding=3 --width=55 --height=3 --at=1000 --no-cursor"
},
"files": [
"index.js",
"index.d.ts"
],
"keywords": [
"ansi",
"styles",
"color",
"colour",
"colors",
"terminal",
"console",
"cli",
"string",
"tty",
"escape",
"formatting",
"rgb",
"256",
"shell",
"xterm",
"log",
"logging",
"command-line",
"text"
],
"dependencies": {
"color-convert": "^2.0.1"
},
"devDependencies": {
"@types/color-convert": "^1.9.0",
"ava": "^2.3.0",
"svg-term-cli": "^2.1.1",
"tsd": "^0.11.0",
"xo": "^0.25.3"
}
}

152
node_modules/karma/node_modules/ansi-styles/readme.md generated vendored Normal file
View file

@ -0,0 +1,152 @@
# ansi-styles [![Build Status](https://travis-ci.org/chalk/ansi-styles.svg?branch=master)](https://travis-ci.org/chalk/ansi-styles)
> [ANSI escape codes](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors_and_Styles) for styling strings in the terminal
You probably want the higher-level [chalk](https://github.com/chalk/chalk) module for styling your strings.
<img src="screenshot.svg" width="900">
## Install
```
$ npm install ansi-styles
```
## Usage
```js
const style = require('ansi-styles');
console.log(`${style.green.open}Hello world!${style.green.close}`);
// Color conversion between 16/256/truecolor
// NOTE: If conversion goes to 16 colors or 256 colors, the original color
// may be degraded to fit that color palette. This means terminals
// that do not support 16 million colors will best-match the
// original color.
console.log(style.bgColor.ansi.hsl(120, 80, 72) + 'Hello world!' + style.bgColor.close);
console.log(style.color.ansi256.rgb(199, 20, 250) + 'Hello world!' + style.color.close);
console.log(style.color.ansi16m.hex('#abcdef') + 'Hello world!' + style.color.close);
```
## API
Each style has an `open` and `close` property.
## Styles
### Modifiers
- `reset`
- `bold`
- `dim`
- `italic` *(Not widely supported)*
- `underline`
- `inverse`
- `hidden`
- `strikethrough` *(Not widely supported)*
### Colors
- `black`
- `red`
- `green`
- `yellow`
- `blue`
- `magenta`
- `cyan`
- `white`
- `blackBright` (alias: `gray`, `grey`)
- `redBright`
- `greenBright`
- `yellowBright`
- `blueBright`
- `magentaBright`
- `cyanBright`
- `whiteBright`
### Background colors
- `bgBlack`
- `bgRed`
- `bgGreen`
- `bgYellow`
- `bgBlue`
- `bgMagenta`
- `bgCyan`
- `bgWhite`
- `bgBlackBright` (alias: `bgGray`, `bgGrey`)
- `bgRedBright`
- `bgGreenBright`
- `bgYellowBright`
- `bgBlueBright`
- `bgMagentaBright`
- `bgCyanBright`
- `bgWhiteBright`
## Advanced usage
By default, you get a map of styles, but the styles are also available as groups. They are non-enumerable so they don't show up unless you access them explicitly. This makes it easier to expose only a subset in a higher-level module.
- `style.modifier`
- `style.color`
- `style.bgColor`
###### Example
```js
console.log(style.color.green.open);
```
Raw escape codes (i.e. without the CSI escape prefix `\u001B[` and render mode postfix `m`) are available under `style.codes`, which returns a `Map` with the open codes as keys and close codes as values.
###### Example
```js
console.log(style.codes.get(36));
//=> 39
```
## [256 / 16 million (TrueColor) support](https://gist.github.com/XVilka/8346728)
`ansi-styles` uses the [`color-convert`](https://github.com/Qix-/color-convert) package to allow for converting between various colors and ANSI escapes, with support for 256 and 16 million colors.
The following color spaces from `color-convert` are supported:
- `rgb`
- `hex`
- `keyword`
- `hsl`
- `hsv`
- `hwb`
- `ansi`
- `ansi256`
To use these, call the associated conversion function with the intended output, for example:
```js
style.color.ansi.rgb(100, 200, 15); // RGB to 16 color ansi foreground code
style.bgColor.ansi.rgb(100, 200, 15); // RGB to 16 color ansi background code
style.color.ansi256.hsl(120, 100, 60); // HSL to 256 color ansi foreground code
style.bgColor.ansi256.hsl(120, 100, 60); // HSL to 256 color ansi foreground code
style.color.ansi16m.hex('#C0FFEE'); // Hex (RGB) to 16 million color foreground code
style.bgColor.ansi16m.hex('#C0FFEE'); // Hex (RGB) to 16 million color background code
```
## Related
- [ansi-escapes](https://github.com/sindresorhus/ansi-escapes) - ANSI escape codes for manipulating the terminal
## Maintainers
- [Sindre Sorhus](https://github.com/sindresorhus)
- [Josh Junon](https://github.com/qix-)
## For enterprise
Available as part of the Tidelift Subscription.
The maintainers of `ansi-styles` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-ansi-styles?utm_source=npm-ansi-styles&utm_medium=referral&utm_campaign=enterprise&utm_term=repo)

121
node_modules/karma/node_modules/cliui/CHANGELOG.md generated vendored Normal file
View file

@ -0,0 +1,121 @@
# 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.
### [7.0.4](https://www.github.com/yargs/cliui/compare/v7.0.3...v7.0.4) (2020-11-08)
### Bug Fixes
* **deno:** import UIOptions from definitions ([#97](https://www.github.com/yargs/cliui/issues/97)) ([f04f343](https://www.github.com/yargs/cliui/commit/f04f3439bc78114c7e90f82ff56f5acf16268ea8))
### [7.0.3](https://www.github.com/yargs/cliui/compare/v7.0.2...v7.0.3) (2020-10-16)
### Bug Fixes
* **exports:** node 13.0 and 13.1 require the dotted object form _with_ a string fallback ([#93](https://www.github.com/yargs/cliui/issues/93)) ([eca16fc](https://www.github.com/yargs/cliui/commit/eca16fc05d26255df3280906c36d7f0e5b05c6e9))
### [7.0.2](https://www.github.com/yargs/cliui/compare/v7.0.1...v7.0.2) (2020-10-14)
### Bug Fixes
* **exports:** node 13.0-13.6 require a string fallback ([#91](https://www.github.com/yargs/cliui/issues/91)) ([b529d7e](https://www.github.com/yargs/cliui/commit/b529d7e432901af1af7848b23ed6cf634497d961))
### [7.0.1](https://www.github.com/yargs/cliui/compare/v7.0.0...v7.0.1) (2020-08-16)
### Bug Fixes
* **build:** main should be build/index.cjs ([dc29a3c](https://www.github.com/yargs/cliui/commit/dc29a3cc617a410aa850e06337b5954b04f2cb4d))
## [7.0.0](https://www.github.com/yargs/cliui/compare/v6.0.0...v7.0.0) (2020-08-16)
### ⚠ BREAKING CHANGES
* tsc/ESM/Deno support (#82)
* modernize deps and build (#80)
### Build System
* modernize deps and build ([#80](https://www.github.com/yargs/cliui/issues/80)) ([339d08d](https://www.github.com/yargs/cliui/commit/339d08dc71b15a3928aeab09042af94db2f43743))
### Code Refactoring
* tsc/ESM/Deno support ([#82](https://www.github.com/yargs/cliui/issues/82)) ([4b777a5](https://www.github.com/yargs/cliui/commit/4b777a5fe01c5d8958c6708695d6aab7dbe5706c))
## [6.0.0](https://www.github.com/yargs/cliui/compare/v5.0.0...v6.0.0) (2019-11-10)
### ⚠ BREAKING CHANGES
* update deps, drop Node 6
### Code Refactoring
* update deps, drop Node 6 ([62056df](https://www.github.com/yargs/cliui/commit/62056df))
## [5.0.0](https://github.com/yargs/cliui/compare/v4.1.0...v5.0.0) (2019-04-10)
### Bug Fixes
* Update wrap-ansi to fix compatibility with latest versions of chalk. ([#60](https://github.com/yargs/cliui/issues/60)) ([7bf79ae](https://github.com/yargs/cliui/commit/7bf79ae))
### BREAKING CHANGES
* Drop support for node < 6.
<a name="4.1.0"></a>
## [4.1.0](https://github.com/yargs/cliui/compare/v4.0.0...v4.1.0) (2018-04-23)
### Features
* add resetOutput method ([#57](https://github.com/yargs/cliui/issues/57)) ([7246902](https://github.com/yargs/cliui/commit/7246902))
<a name="4.0.0"></a>
## [4.0.0](https://github.com/yargs/cliui/compare/v3.2.0...v4.0.0) (2017-12-18)
### Bug Fixes
* downgrades strip-ansi to version 3.0.1 ([#54](https://github.com/yargs/cliui/issues/54)) ([5764c46](https://github.com/yargs/cliui/commit/5764c46))
* set env variable FORCE_COLOR. ([#56](https://github.com/yargs/cliui/issues/56)) ([7350e36](https://github.com/yargs/cliui/commit/7350e36))
### Chores
* drop support for node < 4 ([#53](https://github.com/yargs/cliui/issues/53)) ([b105376](https://github.com/yargs/cliui/commit/b105376))
### Features
* add fallback for window width ([#45](https://github.com/yargs/cliui/issues/45)) ([d064922](https://github.com/yargs/cliui/commit/d064922))
### BREAKING CHANGES
* officially drop support for Node < 4
<a name="3.2.0"></a>
## [3.2.0](https://github.com/yargs/cliui/compare/v3.1.2...v3.2.0) (2016-04-11)
### Bug Fixes
* reduces tarball size ([acc6c33](https://github.com/yargs/cliui/commit/acc6c33))
### Features
* adds standard-version for release management ([ff84e32](https://github.com/yargs/cliui/commit/ff84e32))

14
node_modules/karma/node_modules/cliui/LICENSE.txt generated vendored Normal file
View file

@ -0,0 +1,14 @@
Copyright (c) 2015, 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.

141
node_modules/karma/node_modules/cliui/README.md generated vendored Normal file
View file

@ -0,0 +1,141 @@
# cliui
![ci](https://github.com/yargs/cliui/workflows/ci/badge.svg)
[![NPM version](https://img.shields.io/npm/v/cliui.svg)](https://www.npmjs.com/package/cliui)
[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-yellow.svg)](https://conventionalcommits.org)
![nycrc config on GitHub](https://img.shields.io/nycrc/yargs/cliui)
easily create complex multi-column command-line-interfaces.
## Example
```js
const ui = require('cliui')()
ui.div('Usage: $0 [command] [options]')
ui.div({
text: 'Options:',
padding: [2, 0, 1, 0]
})
ui.div(
{
text: "-f, --file",
width: 20,
padding: [0, 4, 0, 4]
},
{
text: "the file to load." +
chalk.green("(if this description is long it wraps).")
,
width: 20
},
{
text: chalk.red("[required]"),
align: 'right'
}
)
console.log(ui.toString())
```
## Deno/ESM Support
As of `v7` `cliui` supports [Deno](https://github.com/denoland/deno) and
[ESM](https://nodejs.org/api/esm.html#esm_ecmascript_modules):
```typescript
import cliui from "https://deno.land/x/cliui/deno.ts";
const ui = cliui({})
ui.div('Usage: $0 [command] [options]')
ui.div({
text: 'Options:',
padding: [2, 0, 1, 0]
})
ui.div({
text: "-f, --file",
width: 20,
padding: [0, 4, 0, 4]
})
console.log(ui.toString())
```
<img width="500" src="screenshot.png">
## Layout DSL
cliui exposes a simple layout DSL:
If you create a single `ui.div`, passing a string rather than an
object:
* `\n`: characters will be interpreted as new rows.
* `\t`: characters will be interpreted as new columns.
* `\s`: characters will be interpreted as padding.
**as an example...**
```js
var ui = require('./')({
width: 60
})
ui.div(
'Usage: node ./bin/foo.js\n' +
' <regex>\t provide a regex\n' +
' <glob>\t provide a glob\t [required]'
)
console.log(ui.toString())
```
**will output:**
```shell
Usage: node ./bin/foo.js
<regex> provide a regex
<glob> provide a glob [required]
```
## Methods
```js
cliui = require('cliui')
```
### cliui({width: integer})
Specify the maximum width of the UI being generated.
If no width is provided, cliui will try to get the current window's width and use it, and if that doesn't work, width will be set to `80`.
### cliui({wrap: boolean})
Enable or disable the wrapping of text in a column.
### cliui.div(column, column, column)
Create a row with any number of columns, a column
can either be a string, or an object with the following
options:
* **text:** some text to place in the column.
* **width:** the width of a column.
* **align:** alignment, `right` or `center`.
* **padding:** `[top, right, bottom, left]`.
* **border:** should a border be placed around the div?
### cliui.span(column, column, column)
Similar to `div`, except the next row will be appended without
a new line being created.
### cliui.resetOutput()
Resets the UI elements of the current cliui instance, maintaining the values
set for `width` and `wrap`.

302
node_modules/karma/node_modules/cliui/build/index.cjs generated vendored Normal file
View file

@ -0,0 +1,302 @@
'use strict';
const align = {
right: alignRight,
center: alignCenter
};
const top = 0;
const right = 1;
const bottom = 2;
const left = 3;
class UI {
constructor(opts) {
var _a;
this.width = opts.width;
this.wrap = (_a = opts.wrap) !== null && _a !== void 0 ? _a : true;
this.rows = [];
}
span(...args) {
const cols = this.div(...args);
cols.span = true;
}
resetOutput() {
this.rows = [];
}
div(...args) {
if (args.length === 0) {
this.div('');
}
if (this.wrap && this.shouldApplyLayoutDSL(...args) && typeof args[0] === 'string') {
return this.applyLayoutDSL(args[0]);
}
const cols = args.map(arg => {
if (typeof arg === 'string') {
return this.colFromString(arg);
}
return arg;
});
this.rows.push(cols);
return cols;
}
shouldApplyLayoutDSL(...args) {
return args.length === 1 && typeof args[0] === 'string' &&
/[\t\n]/.test(args[0]);
}
applyLayoutDSL(str) {
const rows = str.split('\n').map(row => row.split('\t'));
let leftColumnWidth = 0;
// simple heuristic for layout, make sure the
// second column lines up along the left-hand.
// don't allow the first column to take up more
// than 50% of the screen.
rows.forEach(columns => {
if (columns.length > 1 && mixin.stringWidth(columns[0]) > leftColumnWidth) {
leftColumnWidth = Math.min(Math.floor(this.width * 0.5), mixin.stringWidth(columns[0]));
}
});
// generate a table:
// replacing ' ' with padding calculations.
// using the algorithmically generated width.
rows.forEach(columns => {
this.div(...columns.map((r, i) => {
return {
text: r.trim(),
padding: this.measurePadding(r),
width: (i === 0 && columns.length > 1) ? leftColumnWidth : undefined
};
}));
});
return this.rows[this.rows.length - 1];
}
colFromString(text) {
return {
text,
padding: this.measurePadding(text)
};
}
measurePadding(str) {
// measure padding without ansi escape codes
const noAnsi = mixin.stripAnsi(str);
return [0, noAnsi.match(/\s*$/)[0].length, 0, noAnsi.match(/^\s*/)[0].length];
}
toString() {
const lines = [];
this.rows.forEach(row => {
this.rowToString(row, lines);
});
// don't display any lines with the
// hidden flag set.
return lines
.filter(line => !line.hidden)
.map(line => line.text)
.join('\n');
}
rowToString(row, lines) {
this.rasterize(row).forEach((rrow, r) => {
let str = '';
rrow.forEach((col, c) => {
const { width } = row[c]; // the width with padding.
const wrapWidth = this.negatePadding(row[c]); // the width without padding.
let ts = col; // temporary string used during alignment/padding.
if (wrapWidth > mixin.stringWidth(col)) {
ts += ' '.repeat(wrapWidth - mixin.stringWidth(col));
}
// align the string within its column.
if (row[c].align && row[c].align !== 'left' && this.wrap) {
const fn = align[row[c].align];
ts = fn(ts, wrapWidth);
if (mixin.stringWidth(ts) < wrapWidth) {
ts += ' '.repeat((width || 0) - mixin.stringWidth(ts) - 1);
}
}
// apply border and padding to string.
const padding = row[c].padding || [0, 0, 0, 0];
if (padding[left]) {
str += ' '.repeat(padding[left]);
}
str += addBorder(row[c], ts, '| ');
str += ts;
str += addBorder(row[c], ts, ' |');
if (padding[right]) {
str += ' '.repeat(padding[right]);
}
// if prior row is span, try to render the
// current row on the prior line.
if (r === 0 && lines.length > 0) {
str = this.renderInline(str, lines[lines.length - 1]);
}
});
// remove trailing whitespace.
lines.push({
text: str.replace(/ +$/, ''),
span: row.span
});
});
return lines;
}
// if the full 'source' can render in
// the target line, do so.
renderInline(source, previousLine) {
const match = source.match(/^ */);
const leadingWhitespace = match ? match[0].length : 0;
const target = previousLine.text;
const targetTextWidth = mixin.stringWidth(target.trimRight());
if (!previousLine.span) {
return source;
}
// if we're not applying wrapping logic,
// just always append to the span.
if (!this.wrap) {
previousLine.hidden = true;
return target + source;
}
if (leadingWhitespace < targetTextWidth) {
return source;
}
previousLine.hidden = true;
return target.trimRight() + ' '.repeat(leadingWhitespace - targetTextWidth) + source.trimLeft();
}
rasterize(row) {
const rrows = [];
const widths = this.columnWidths(row);
let wrapped;
// word wrap all columns, and create
// a data-structure that is easy to rasterize.
row.forEach((col, c) => {
// leave room for left and right padding.
col.width = widths[c];
if (this.wrap) {
wrapped = mixin.wrap(col.text, this.negatePadding(col), { hard: true }).split('\n');
}
else {
wrapped = col.text.split('\n');
}
if (col.border) {
wrapped.unshift('.' + '-'.repeat(this.negatePadding(col) + 2) + '.');
wrapped.push("'" + '-'.repeat(this.negatePadding(col) + 2) + "'");
}
// add top and bottom padding.
if (col.padding) {
wrapped.unshift(...new Array(col.padding[top] || 0).fill(''));
wrapped.push(...new Array(col.padding[bottom] || 0).fill(''));
}
wrapped.forEach((str, r) => {
if (!rrows[r]) {
rrows.push([]);
}
const rrow = rrows[r];
for (let i = 0; i < c; i++) {
if (rrow[i] === undefined) {
rrow.push('');
}
}
rrow.push(str);
});
});
return rrows;
}
negatePadding(col) {
let wrapWidth = col.width || 0;
if (col.padding) {
wrapWidth -= (col.padding[left] || 0) + (col.padding[right] || 0);
}
if (col.border) {
wrapWidth -= 4;
}
return wrapWidth;
}
columnWidths(row) {
if (!this.wrap) {
return row.map(col => {
return col.width || mixin.stringWidth(col.text);
});
}
let unset = row.length;
let remainingWidth = this.width;
// column widths can be set in config.
const widths = row.map(col => {
if (col.width) {
unset--;
remainingWidth -= col.width;
return col.width;
}
return undefined;
});
// any unset widths should be calculated.
const unsetWidth = unset ? Math.floor(remainingWidth / unset) : 0;
return widths.map((w, i) => {
if (w === undefined) {
return Math.max(unsetWidth, _minWidth(row[i]));
}
return w;
});
}
}
function addBorder(col, ts, style) {
if (col.border) {
if (/[.']-+[.']/.test(ts)) {
return '';
}
if (ts.trim().length !== 0) {
return style;
}
return ' ';
}
return '';
}
// calculates the minimum width of
// a column, based on padding preferences.
function _minWidth(col) {
const padding = col.padding || [];
const minWidth = 1 + (padding[left] || 0) + (padding[right] || 0);
if (col.border) {
return minWidth + 4;
}
return minWidth;
}
function getWindowWidth() {
/* istanbul ignore next: depends on terminal */
if (typeof process === 'object' && process.stdout && process.stdout.columns) {
return process.stdout.columns;
}
return 80;
}
function alignRight(str, width) {
str = str.trim();
const strWidth = mixin.stringWidth(str);
if (strWidth < width) {
return ' '.repeat(width - strWidth) + str;
}
return str;
}
function alignCenter(str, width) {
str = str.trim();
const strWidth = mixin.stringWidth(str);
/* istanbul ignore next */
if (strWidth >= width) {
return str;
}
return ' '.repeat((width - strWidth) >> 1) + str;
}
let mixin;
function cliui(opts, _mixin) {
mixin = _mixin;
return new UI({
width: (opts === null || opts === void 0 ? void 0 : opts.width) || getWindowWidth(),
wrap: opts === null || opts === void 0 ? void 0 : opts.wrap
});
}
// Bootstrap cliui with CommonJS dependencies:
const stringWidth = require('string-width');
const stripAnsi = require('strip-ansi');
const wrap = require('wrap-ansi');
function ui(opts) {
return cliui(opts, {
stringWidth,
stripAnsi,
wrap
});
}
module.exports = ui;

View file

@ -0,0 +1,287 @@
'use strict';
const align = {
right: alignRight,
center: alignCenter
};
const top = 0;
const right = 1;
const bottom = 2;
const left = 3;
export class UI {
constructor(opts) {
var _a;
this.width = opts.width;
this.wrap = (_a = opts.wrap) !== null && _a !== void 0 ? _a : true;
this.rows = [];
}
span(...args) {
const cols = this.div(...args);
cols.span = true;
}
resetOutput() {
this.rows = [];
}
div(...args) {
if (args.length === 0) {
this.div('');
}
if (this.wrap && this.shouldApplyLayoutDSL(...args) && typeof args[0] === 'string') {
return this.applyLayoutDSL(args[0]);
}
const cols = args.map(arg => {
if (typeof arg === 'string') {
return this.colFromString(arg);
}
return arg;
});
this.rows.push(cols);
return cols;
}
shouldApplyLayoutDSL(...args) {
return args.length === 1 && typeof args[0] === 'string' &&
/[\t\n]/.test(args[0]);
}
applyLayoutDSL(str) {
const rows = str.split('\n').map(row => row.split('\t'));
let leftColumnWidth = 0;
// simple heuristic for layout, make sure the
// second column lines up along the left-hand.
// don't allow the first column to take up more
// than 50% of the screen.
rows.forEach(columns => {
if (columns.length > 1 && mixin.stringWidth(columns[0]) > leftColumnWidth) {
leftColumnWidth = Math.min(Math.floor(this.width * 0.5), mixin.stringWidth(columns[0]));
}
});
// generate a table:
// replacing ' ' with padding calculations.
// using the algorithmically generated width.
rows.forEach(columns => {
this.div(...columns.map((r, i) => {
return {
text: r.trim(),
padding: this.measurePadding(r),
width: (i === 0 && columns.length > 1) ? leftColumnWidth : undefined
};
}));
});
return this.rows[this.rows.length - 1];
}
colFromString(text) {
return {
text,
padding: this.measurePadding(text)
};
}
measurePadding(str) {
// measure padding without ansi escape codes
const noAnsi = mixin.stripAnsi(str);
return [0, noAnsi.match(/\s*$/)[0].length, 0, noAnsi.match(/^\s*/)[0].length];
}
toString() {
const lines = [];
this.rows.forEach(row => {
this.rowToString(row, lines);
});
// don't display any lines with the
// hidden flag set.
return lines
.filter(line => !line.hidden)
.map(line => line.text)
.join('\n');
}
rowToString(row, lines) {
this.rasterize(row).forEach((rrow, r) => {
let str = '';
rrow.forEach((col, c) => {
const { width } = row[c]; // the width with padding.
const wrapWidth = this.negatePadding(row[c]); // the width without padding.
let ts = col; // temporary string used during alignment/padding.
if (wrapWidth > mixin.stringWidth(col)) {
ts += ' '.repeat(wrapWidth - mixin.stringWidth(col));
}
// align the string within its column.
if (row[c].align && row[c].align !== 'left' && this.wrap) {
const fn = align[row[c].align];
ts = fn(ts, wrapWidth);
if (mixin.stringWidth(ts) < wrapWidth) {
ts += ' '.repeat((width || 0) - mixin.stringWidth(ts) - 1);
}
}
// apply border and padding to string.
const padding = row[c].padding || [0, 0, 0, 0];
if (padding[left]) {
str += ' '.repeat(padding[left]);
}
str += addBorder(row[c], ts, '| ');
str += ts;
str += addBorder(row[c], ts, ' |');
if (padding[right]) {
str += ' '.repeat(padding[right]);
}
// if prior row is span, try to render the
// current row on the prior line.
if (r === 0 && lines.length > 0) {
str = this.renderInline(str, lines[lines.length - 1]);
}
});
// remove trailing whitespace.
lines.push({
text: str.replace(/ +$/, ''),
span: row.span
});
});
return lines;
}
// if the full 'source' can render in
// the target line, do so.
renderInline(source, previousLine) {
const match = source.match(/^ */);
const leadingWhitespace = match ? match[0].length : 0;
const target = previousLine.text;
const targetTextWidth = mixin.stringWidth(target.trimRight());
if (!previousLine.span) {
return source;
}
// if we're not applying wrapping logic,
// just always append to the span.
if (!this.wrap) {
previousLine.hidden = true;
return target + source;
}
if (leadingWhitespace < targetTextWidth) {
return source;
}
previousLine.hidden = true;
return target.trimRight() + ' '.repeat(leadingWhitespace - targetTextWidth) + source.trimLeft();
}
rasterize(row) {
const rrows = [];
const widths = this.columnWidths(row);
let wrapped;
// word wrap all columns, and create
// a data-structure that is easy to rasterize.
row.forEach((col, c) => {
// leave room for left and right padding.
col.width = widths[c];
if (this.wrap) {
wrapped = mixin.wrap(col.text, this.negatePadding(col), { hard: true }).split('\n');
}
else {
wrapped = col.text.split('\n');
}
if (col.border) {
wrapped.unshift('.' + '-'.repeat(this.negatePadding(col) + 2) + '.');
wrapped.push("'" + '-'.repeat(this.negatePadding(col) + 2) + "'");
}
// add top and bottom padding.
if (col.padding) {
wrapped.unshift(...new Array(col.padding[top] || 0).fill(''));
wrapped.push(...new Array(col.padding[bottom] || 0).fill(''));
}
wrapped.forEach((str, r) => {
if (!rrows[r]) {
rrows.push([]);
}
const rrow = rrows[r];
for (let i = 0; i < c; i++) {
if (rrow[i] === undefined) {
rrow.push('');
}
}
rrow.push(str);
});
});
return rrows;
}
negatePadding(col) {
let wrapWidth = col.width || 0;
if (col.padding) {
wrapWidth -= (col.padding[left] || 0) + (col.padding[right] || 0);
}
if (col.border) {
wrapWidth -= 4;
}
return wrapWidth;
}
columnWidths(row) {
if (!this.wrap) {
return row.map(col => {
return col.width || mixin.stringWidth(col.text);
});
}
let unset = row.length;
let remainingWidth = this.width;
// column widths can be set in config.
const widths = row.map(col => {
if (col.width) {
unset--;
remainingWidth -= col.width;
return col.width;
}
return undefined;
});
// any unset widths should be calculated.
const unsetWidth = unset ? Math.floor(remainingWidth / unset) : 0;
return widths.map((w, i) => {
if (w === undefined) {
return Math.max(unsetWidth, _minWidth(row[i]));
}
return w;
});
}
}
function addBorder(col, ts, style) {
if (col.border) {
if (/[.']-+[.']/.test(ts)) {
return '';
}
if (ts.trim().length !== 0) {
return style;
}
return ' ';
}
return '';
}
// calculates the minimum width of
// a column, based on padding preferences.
function _minWidth(col) {
const padding = col.padding || [];
const minWidth = 1 + (padding[left] || 0) + (padding[right] || 0);
if (col.border) {
return minWidth + 4;
}
return minWidth;
}
function getWindowWidth() {
/* istanbul ignore next: depends on terminal */
if (typeof process === 'object' && process.stdout && process.stdout.columns) {
return process.stdout.columns;
}
return 80;
}
function alignRight(str, width) {
str = str.trim();
const strWidth = mixin.stringWidth(str);
if (strWidth < width) {
return ' '.repeat(width - strWidth) + str;
}
return str;
}
function alignCenter(str, width) {
str = str.trim();
const strWidth = mixin.stringWidth(str);
/* istanbul ignore next */
if (strWidth >= width) {
return str;
}
return ' '.repeat((width - strWidth) >> 1) + str;
}
let mixin;
export function cliui(opts, _mixin) {
mixin = _mixin;
return new UI({
width: (opts === null || opts === void 0 ? void 0 : opts.width) || getWindowWidth(),
wrap: opts === null || opts === void 0 ? void 0 : opts.wrap
});
}

View file

@ -0,0 +1,27 @@
// Minimal replacement for ansi string helpers "wrap-ansi" and "strip-ansi".
// to facilitate ESM and Deno modules.
// TODO: look at porting https://www.npmjs.com/package/wrap-ansi to ESM.
// The npm application
// Copyright (c) npm, Inc. and Contributors
// Licensed on the terms of The Artistic License 2.0
// See: https://github.com/npm/cli/blob/4c65cd952bc8627811735bea76b9b110cc4fc80e/lib/utils/ansi-trim.js
const ansi = new RegExp('\x1b(?:\\[(?:\\d+[ABCDEFGJKSTm]|\\d+;\\d+[Hfm]|' +
'\\d+;\\d+;\\d+m|6n|s|u|\\?25[lh])|\\w)', 'g');
export function stripAnsi(str) {
return str.replace(ansi, '');
}
export function wrap(str, width) {
const [start, end] = str.match(ansi) || ['', ''];
str = stripAnsi(str);
let wrapped = '';
for (let i = 0; i < str.length; i++) {
if (i !== 0 && (i % width) === 0) {
wrapped += '\n';
}
wrapped += str.charAt(i);
}
if (start && end) {
wrapped = `${start}${wrapped}${end}`;
}
return wrapped;
}

13
node_modules/karma/node_modules/cliui/index.mjs generated vendored Normal file
View file

@ -0,0 +1,13 @@
// Bootstrap cliui with CommonJS dependencies:
import { cliui } from './build/lib/index.js'
import { wrap, stripAnsi } from './build/lib/string-utils.js'
export default function ui (opts) {
return cliui(opts, {
stringWidth: (str) => {
return [...str].length
},
stripAnsi,
wrap
})
}

83
node_modules/karma/node_modules/cliui/package.json generated vendored Normal file
View file

@ -0,0 +1,83 @@
{
"name": "cliui",
"version": "7.0.4",
"description": "easily create complex multi-column command-line-interfaces",
"main": "build/index.cjs",
"exports": {
".": [
{
"import": "./index.mjs",
"require": "./build/index.cjs"
},
"./build/index.cjs"
]
},
"type": "module",
"module": "./index.mjs",
"scripts": {
"check": "standardx '**/*.ts' && standardx '**/*.js' && standardx '**/*.cjs'",
"fix": "standardx --fix '**/*.ts' && standardx --fix '**/*.js' && standardx --fix '**/*.cjs'",
"pretest": "rimraf build && tsc -p tsconfig.test.json && cross-env NODE_ENV=test npm run build:cjs",
"test": "c8 mocha ./test/*.cjs",
"test:esm": "c8 mocha ./test/esm/cliui-test.mjs",
"postest": "check",
"coverage": "c8 report --check-coverage",
"precompile": "rimraf build",
"compile": "tsc",
"postcompile": "npm run build:cjs",
"build:cjs": "rollup -c",
"prepare": "npm run compile"
},
"repository": "yargs/cliui",
"standard": {
"ignore": [
"**/example/**"
],
"globals": [
"it"
]
},
"keywords": [
"cli",
"command-line",
"layout",
"design",
"console",
"wrap",
"table"
],
"author": "Ben Coe <ben@npmjs.com>",
"license": "ISC",
"dependencies": {
"string-width": "^4.2.0",
"strip-ansi": "^6.0.0",
"wrap-ansi": "^7.0.0"
},
"devDependencies": {
"@types/node": "^14.0.27",
"@typescript-eslint/eslint-plugin": "^4.0.0",
"@typescript-eslint/parser": "^4.0.0",
"@wessberg/rollup-plugin-ts": "^1.3.2",
"c8": "^7.3.0",
"chai": "^4.2.0",
"chalk": "^4.1.0",
"cross-env": "^7.0.2",
"eslint": "^7.6.0",
"eslint-plugin-import": "^2.22.0",
"eslint-plugin-node": "^11.1.0",
"gts": "^3.0.0",
"mocha": "^8.1.1",
"rimraf": "^3.0.2",
"rollup": "^2.23.1",
"standardx": "^7.0.0",
"typescript": "^4.0.0"
},
"files": [
"build",
"index.mjs",
"!*.d.ts"
],
"engine": {
"node": ">=10"
}
}

View file

@ -0,0 +1,54 @@
# 1.0.0 - 2016-01-07
- Removed: unused speed test
- Added: Automatic routing between previously unsupported conversions
([#27](https://github.com/Qix-/color-convert/pull/27))
- Removed: `xxx2xxx()` and `xxx2xxxRaw()` functions
([#27](https://github.com/Qix-/color-convert/pull/27))
- Removed: `convert()` class
([#27](https://github.com/Qix-/color-convert/pull/27))
- Changed: all functions to lookup dictionary
([#27](https://github.com/Qix-/color-convert/pull/27))
- Changed: `ansi` to `ansi256`
([#27](https://github.com/Qix-/color-convert/pull/27))
- Fixed: argument grouping for functions requiring only one argument
([#27](https://github.com/Qix-/color-convert/pull/27))
# 0.6.0 - 2015-07-23
- Added: methods to handle
[ANSI](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors) 16/256 colors:
- rgb2ansi16
- rgb2ansi
- hsl2ansi16
- hsl2ansi
- hsv2ansi16
- hsv2ansi
- hwb2ansi16
- hwb2ansi
- cmyk2ansi16
- cmyk2ansi
- keyword2ansi16
- keyword2ansi
- ansi162rgb
- ansi162hsl
- ansi162hsv
- ansi162hwb
- ansi162cmyk
- ansi162keyword
- ansi2rgb
- ansi2hsl
- ansi2hsv
- ansi2hwb
- ansi2cmyk
- ansi2keyword
([#18](https://github.com/harthur/color-convert/pull/18))
# 0.5.3 - 2015-06-02
- Fixed: hsl2hsv does not return `NaN` anymore when using `[0,0,0]`
([#15](https://github.com/harthur/color-convert/issues/15))
---
Check out commit logs for older releases

21
node_modules/karma/node_modules/color-convert/LICENSE generated vendored Normal file
View file

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

View file

@ -0,0 +1,68 @@
# color-convert
[![Build Status](https://travis-ci.org/Qix-/color-convert.svg?branch=master)](https://travis-ci.org/Qix-/color-convert)
Color-convert is a color conversion library for JavaScript and node.
It converts all ways between `rgb`, `hsl`, `hsv`, `hwb`, `cmyk`, `ansi`, `ansi16`, `hex` strings, and CSS `keyword`s (will round to closest):
```js
var convert = require('color-convert');
convert.rgb.hsl(140, 200, 100); // [96, 48, 59]
convert.keyword.rgb('blue'); // [0, 0, 255]
var rgbChannels = convert.rgb.channels; // 3
var cmykChannels = convert.cmyk.channels; // 4
var ansiChannels = convert.ansi16.channels; // 1
```
# Install
```console
$ npm install color-convert
```
# API
Simply get the property of the _from_ and _to_ conversion that you're looking for.
All functions have a rounded and unrounded variant. By default, return values are rounded. To get the unrounded (raw) results, simply tack on `.raw` to the function.
All 'from' functions have a hidden property called `.channels` that indicates the number of channels the function expects (not including alpha).
```js
var convert = require('color-convert');
// Hex to LAB
convert.hex.lab('DEADBF'); // [ 76, 21, -2 ]
convert.hex.lab.raw('DEADBF'); // [ 75.56213190997677, 20.653827952644754, -2.290532499330533 ]
// RGB to CMYK
convert.rgb.cmyk(167, 255, 4); // [ 35, 0, 98, 0 ]
convert.rgb.cmyk.raw(167, 255, 4); // [ 34.509803921568626, 0, 98.43137254901961, 0 ]
```
### Arrays
All functions that accept multiple arguments also support passing an array.
Note that this does **not** apply to functions that convert from a color that only requires one value (e.g. `keyword`, `ansi256`, `hex`, etc.)
```js
var convert = require('color-convert');
convert.rgb.hex(123, 45, 67); // '7B2D43'
convert.rgb.hex([123, 45, 67]); // '7B2D43'
```
## Routing
Conversions that don't have an _explicitly_ defined conversion (in [conversions.js](conversions.js)), but can be converted by means of sub-conversions (e.g. XYZ -> **RGB** -> CMYK), are automatically routed together. This allows just about any color model supported by `color-convert` to be converted to any other model, so long as a sub-conversion path exists. This is also true for conversions requiring more than one step in between (e.g. LCH -> **LAB** -> **XYZ** -> **RGB** -> Hex).
Keep in mind that extensive conversions _may_ result in a loss of precision, and exist only to be complete. For a list of "direct" (single-step) conversions, see [conversions.js](conversions.js).
# Contribute
If there is a new model you would like to support, or want to add a direct conversion between two existing models, please send us a pull request.
# License
Copyright &copy; 2011-2016, Heather Arthur and Josh Junon. Licensed under the [MIT License](LICENSE).

View file

@ -0,0 +1,839 @@
/* MIT license */
/* eslint-disable no-mixed-operators */
const cssKeywords = require('color-name');
// NOTE: conversions should only return primitive values (i.e. arrays, or
// values that give correct `typeof` results).
// do not use box values types (i.e. Number(), String(), etc.)
const reverseKeywords = {};
for (const key of Object.keys(cssKeywords)) {
reverseKeywords[cssKeywords[key]] = key;
}
const convert = {
rgb: {channels: 3, labels: 'rgb'},
hsl: {channels: 3, labels: 'hsl'},
hsv: {channels: 3, labels: 'hsv'},
hwb: {channels: 3, labels: 'hwb'},
cmyk: {channels: 4, labels: 'cmyk'},
xyz: {channels: 3, labels: 'xyz'},
lab: {channels: 3, labels: 'lab'},
lch: {channels: 3, labels: 'lch'},
hex: {channels: 1, labels: ['hex']},
keyword: {channels: 1, labels: ['keyword']},
ansi16: {channels: 1, labels: ['ansi16']},
ansi256: {channels: 1, labels: ['ansi256']},
hcg: {channels: 3, labels: ['h', 'c', 'g']},
apple: {channels: 3, labels: ['r16', 'g16', 'b16']},
gray: {channels: 1, labels: ['gray']}
};
module.exports = convert;
// Hide .channels and .labels properties
for (const model of Object.keys(convert)) {
if (!('channels' in convert[model])) {
throw new Error('missing channels property: ' + model);
}
if (!('labels' in convert[model])) {
throw new Error('missing channel labels property: ' + model);
}
if (convert[model].labels.length !== convert[model].channels) {
throw new Error('channel and label counts mismatch: ' + model);
}
const {channels, labels} = convert[model];
delete convert[model].channels;
delete convert[model].labels;
Object.defineProperty(convert[model], 'channels', {value: channels});
Object.defineProperty(convert[model], 'labels', {value: labels});
}
convert.rgb.hsl = function (rgb) {
const r = rgb[0] / 255;
const g = rgb[1] / 255;
const b = rgb[2] / 255;
const min = Math.min(r, g, b);
const max = Math.max(r, g, b);
const delta = max - min;
let h;
let s;
if (max === min) {
h = 0;
} else if (r === max) {
h = (g - b) / delta;
} else if (g === max) {
h = 2 + (b - r) / delta;
} else if (b === max) {
h = 4 + (r - g) / delta;
}
h = Math.min(h * 60, 360);
if (h < 0) {
h += 360;
}
const l = (min + max) / 2;
if (max === min) {
s = 0;
} else if (l <= 0.5) {
s = delta / (max + min);
} else {
s = delta / (2 - max - min);
}
return [h, s * 100, l * 100];
};
convert.rgb.hsv = function (rgb) {
let rdif;
let gdif;
let bdif;
let h;
let s;
const r = rgb[0] / 255;
const g = rgb[1] / 255;
const b = rgb[2] / 255;
const v = Math.max(r, g, b);
const diff = v - Math.min(r, g, b);
const diffc = function (c) {
return (v - c) / 6 / diff + 1 / 2;
};
if (diff === 0) {
h = 0;
s = 0;
} else {
s = diff / v;
rdif = diffc(r);
gdif = diffc(g);
bdif = diffc(b);
if (r === v) {
h = bdif - gdif;
} else if (g === v) {
h = (1 / 3) + rdif - bdif;
} else if (b === v) {
h = (2 / 3) + gdif - rdif;
}
if (h < 0) {
h += 1;
} else if (h > 1) {
h -= 1;
}
}
return [
h * 360,
s * 100,
v * 100
];
};
convert.rgb.hwb = function (rgb) {
const r = rgb[0];
const g = rgb[1];
let b = rgb[2];
const h = convert.rgb.hsl(rgb)[0];
const w = 1 / 255 * Math.min(r, Math.min(g, b));
b = 1 - 1 / 255 * Math.max(r, Math.max(g, b));
return [h, w * 100, b * 100];
};
convert.rgb.cmyk = function (rgb) {
const r = rgb[0] / 255;
const g = rgb[1] / 255;
const b = rgb[2] / 255;
const k = Math.min(1 - r, 1 - g, 1 - b);
const c = (1 - r - k) / (1 - k) || 0;
const m = (1 - g - k) / (1 - k) || 0;
const y = (1 - b - k) / (1 - k) || 0;
return [c * 100, m * 100, y * 100, k * 100];
};
function comparativeDistance(x, y) {
/*
See https://en.m.wikipedia.org/wiki/Euclidean_distance#Squared_Euclidean_distance
*/
return (
((x[0] - y[0]) ** 2) +
((x[1] - y[1]) ** 2) +
((x[2] - y[2]) ** 2)
);
}
convert.rgb.keyword = function (rgb) {
const reversed = reverseKeywords[rgb];
if (reversed) {
return reversed;
}
let currentClosestDistance = Infinity;
let currentClosestKeyword;
for (const keyword of Object.keys(cssKeywords)) {
const value = cssKeywords[keyword];
// Compute comparative distance
const distance = comparativeDistance(rgb, value);
// Check if its less, if so set as closest
if (distance < currentClosestDistance) {
currentClosestDistance = distance;
currentClosestKeyword = keyword;
}
}
return currentClosestKeyword;
};
convert.keyword.rgb = function (keyword) {
return cssKeywords[keyword];
};
convert.rgb.xyz = function (rgb) {
let r = rgb[0] / 255;
let g = rgb[1] / 255;
let b = rgb[2] / 255;
// Assume sRGB
r = r > 0.04045 ? (((r + 0.055) / 1.055) ** 2.4) : (r / 12.92);
g = g > 0.04045 ? (((g + 0.055) / 1.055) ** 2.4) : (g / 12.92);
b = b > 0.04045 ? (((b + 0.055) / 1.055) ** 2.4) : (b / 12.92);
const x = (r * 0.4124) + (g * 0.3576) + (b * 0.1805);
const y = (r * 0.2126) + (g * 0.7152) + (b * 0.0722);
const z = (r * 0.0193) + (g * 0.1192) + (b * 0.9505);
return [x * 100, y * 100, z * 100];
};
convert.rgb.lab = function (rgb) {
const xyz = convert.rgb.xyz(rgb);
let x = xyz[0];
let y = xyz[1];
let z = xyz[2];
x /= 95.047;
y /= 100;
z /= 108.883;
x = x > 0.008856 ? (x ** (1 / 3)) : (7.787 * x) + (16 / 116);
y = y > 0.008856 ? (y ** (1 / 3)) : (7.787 * y) + (16 / 116);
z = z > 0.008856 ? (z ** (1 / 3)) : (7.787 * z) + (16 / 116);
const l = (116 * y) - 16;
const a = 500 * (x - y);
const b = 200 * (y - z);
return [l, a, b];
};
convert.hsl.rgb = function (hsl) {
const h = hsl[0] / 360;
const s = hsl[1] / 100;
const l = hsl[2] / 100;
let t2;
let t3;
let val;
if (s === 0) {
val = l * 255;
return [val, val, val];
}
if (l < 0.5) {
t2 = l * (1 + s);
} else {
t2 = l + s - l * s;
}
const t1 = 2 * l - t2;
const rgb = [0, 0, 0];
for (let i = 0; i < 3; i++) {
t3 = h + 1 / 3 * -(i - 1);
if (t3 < 0) {
t3++;
}
if (t3 > 1) {
t3--;
}
if (6 * t3 < 1) {
val = t1 + (t2 - t1) * 6 * t3;
} else if (2 * t3 < 1) {
val = t2;
} else if (3 * t3 < 2) {
val = t1 + (t2 - t1) * (2 / 3 - t3) * 6;
} else {
val = t1;
}
rgb[i] = val * 255;
}
return rgb;
};
convert.hsl.hsv = function (hsl) {
const h = hsl[0];
let s = hsl[1] / 100;
let l = hsl[2] / 100;
let smin = s;
const lmin = Math.max(l, 0.01);
l *= 2;
s *= (l <= 1) ? l : 2 - l;
smin *= lmin <= 1 ? lmin : 2 - lmin;
const v = (l + s) / 2;
const sv = l === 0 ? (2 * smin) / (lmin + smin) : (2 * s) / (l + s);
return [h, sv * 100, v * 100];
};
convert.hsv.rgb = function (hsv) {
const h = hsv[0] / 60;
const s = hsv[1] / 100;
let v = hsv[2] / 100;
const hi = Math.floor(h) % 6;
const f = h - Math.floor(h);
const p = 255 * v * (1 - s);
const q = 255 * v * (1 - (s * f));
const t = 255 * v * (1 - (s * (1 - f)));
v *= 255;
switch (hi) {
case 0:
return [v, t, p];
case 1:
return [q, v, p];
case 2:
return [p, v, t];
case 3:
return [p, q, v];
case 4:
return [t, p, v];
case 5:
return [v, p, q];
}
};
convert.hsv.hsl = function (hsv) {
const h = hsv[0];
const s = hsv[1] / 100;
const v = hsv[2] / 100;
const vmin = Math.max(v, 0.01);
let sl;
let l;
l = (2 - s) * v;
const lmin = (2 - s) * vmin;
sl = s * vmin;
sl /= (lmin <= 1) ? lmin : 2 - lmin;
sl = sl || 0;
l /= 2;
return [h, sl * 100, l * 100];
};
// http://dev.w3.org/csswg/css-color/#hwb-to-rgb
convert.hwb.rgb = function (hwb) {
const h = hwb[0] / 360;
let wh = hwb[1] / 100;
let bl = hwb[2] / 100;
const ratio = wh + bl;
let f;
// Wh + bl cant be > 1
if (ratio > 1) {
wh /= ratio;
bl /= ratio;
}
const i = Math.floor(6 * h);
const v = 1 - bl;
f = 6 * h - i;
if ((i & 0x01) !== 0) {
f = 1 - f;
}
const n = wh + f * (v - wh); // Linear interpolation
let r;
let g;
let b;
/* eslint-disable max-statements-per-line,no-multi-spaces */
switch (i) {
default:
case 6:
case 0: r = v; g = n; b = wh; break;
case 1: r = n; g = v; b = wh; break;
case 2: r = wh; g = v; b = n; break;
case 3: r = wh; g = n; b = v; break;
case 4: r = n; g = wh; b = v; break;
case 5: r = v; g = wh; b = n; break;
}
/* eslint-enable max-statements-per-line,no-multi-spaces */
return [r * 255, g * 255, b * 255];
};
convert.cmyk.rgb = function (cmyk) {
const c = cmyk[0] / 100;
const m = cmyk[1] / 100;
const y = cmyk[2] / 100;
const k = cmyk[3] / 100;
const r = 1 - Math.min(1, c * (1 - k) + k);
const g = 1 - Math.min(1, m * (1 - k) + k);
const b = 1 - Math.min(1, y * (1 - k) + k);
return [r * 255, g * 255, b * 255];
};
convert.xyz.rgb = function (xyz) {
const x = xyz[0] / 100;
const y = xyz[1] / 100;
const z = xyz[2] / 100;
let r;
let g;
let b;
r = (x * 3.2406) + (y * -1.5372) + (z * -0.4986);
g = (x * -0.9689) + (y * 1.8758) + (z * 0.0415);
b = (x * 0.0557) + (y * -0.2040) + (z * 1.0570);
// Assume sRGB
r = r > 0.0031308
? ((1.055 * (r ** (1.0 / 2.4))) - 0.055)
: r * 12.92;
g = g > 0.0031308
? ((1.055 * (g ** (1.0 / 2.4))) - 0.055)
: g * 12.92;
b = b > 0.0031308
? ((1.055 * (b ** (1.0 / 2.4))) - 0.055)
: b * 12.92;
r = Math.min(Math.max(0, r), 1);
g = Math.min(Math.max(0, g), 1);
b = Math.min(Math.max(0, b), 1);
return [r * 255, g * 255, b * 255];
};
convert.xyz.lab = function (xyz) {
let x = xyz[0];
let y = xyz[1];
let z = xyz[2];
x /= 95.047;
y /= 100;
z /= 108.883;
x = x > 0.008856 ? (x ** (1 / 3)) : (7.787 * x) + (16 / 116);
y = y > 0.008856 ? (y ** (1 / 3)) : (7.787 * y) + (16 / 116);
z = z > 0.008856 ? (z ** (1 / 3)) : (7.787 * z) + (16 / 116);
const l = (116 * y) - 16;
const a = 500 * (x - y);
const b = 200 * (y - z);
return [l, a, b];
};
convert.lab.xyz = function (lab) {
const l = lab[0];
const a = lab[1];
const b = lab[2];
let x;
let y;
let z;
y = (l + 16) / 116;
x = a / 500 + y;
z = y - b / 200;
const y2 = y ** 3;
const x2 = x ** 3;
const z2 = z ** 3;
y = y2 > 0.008856 ? y2 : (y - 16 / 116) / 7.787;
x = x2 > 0.008856 ? x2 : (x - 16 / 116) / 7.787;
z = z2 > 0.008856 ? z2 : (z - 16 / 116) / 7.787;
x *= 95.047;
y *= 100;
z *= 108.883;
return [x, y, z];
};
convert.lab.lch = function (lab) {
const l = lab[0];
const a = lab[1];
const b = lab[2];
let h;
const hr = Math.atan2(b, a);
h = hr * 360 / 2 / Math.PI;
if (h < 0) {
h += 360;
}
const c = Math.sqrt(a * a + b * b);
return [l, c, h];
};
convert.lch.lab = function (lch) {
const l = lch[0];
const c = lch[1];
const h = lch[2];
const hr = h / 360 * 2 * Math.PI;
const a = c * Math.cos(hr);
const b = c * Math.sin(hr);
return [l, a, b];
};
convert.rgb.ansi16 = function (args, saturation = null) {
const [r, g, b] = args;
let value = saturation === null ? convert.rgb.hsv(args)[2] : saturation; // Hsv -> ansi16 optimization
value = Math.round(value / 50);
if (value === 0) {
return 30;
}
let ansi = 30
+ ((Math.round(b / 255) << 2)
| (Math.round(g / 255) << 1)
| Math.round(r / 255));
if (value === 2) {
ansi += 60;
}
return ansi;
};
convert.hsv.ansi16 = function (args) {
// Optimization here; we already know the value and don't need to get
// it converted for us.
return convert.rgb.ansi16(convert.hsv.rgb(args), args[2]);
};
convert.rgb.ansi256 = function (args) {
const r = args[0];
const g = args[1];
const b = args[2];
// We use the extended greyscale palette here, with the exception of
// black and white. normal palette only has 4 greyscale shades.
if (r === g && g === b) {
if (r < 8) {
return 16;
}
if (r > 248) {
return 231;
}
return Math.round(((r - 8) / 247) * 24) + 232;
}
const ansi = 16
+ (36 * Math.round(r / 255 * 5))
+ (6 * Math.round(g / 255 * 5))
+ Math.round(b / 255 * 5);
return ansi;
};
convert.ansi16.rgb = function (args) {
let color = args % 10;
// Handle greyscale
if (color === 0 || color === 7) {
if (args > 50) {
color += 3.5;
}
color = color / 10.5 * 255;
return [color, color, color];
}
const mult = (~~(args > 50) + 1) * 0.5;
const r = ((color & 1) * mult) * 255;
const g = (((color >> 1) & 1) * mult) * 255;
const b = (((color >> 2) & 1) * mult) * 255;
return [r, g, b];
};
convert.ansi256.rgb = function (args) {
// Handle greyscale
if (args >= 232) {
const c = (args - 232) * 10 + 8;
return [c, c, c];
}
args -= 16;
let rem;
const r = Math.floor(args / 36) / 5 * 255;
const g = Math.floor((rem = args % 36) / 6) / 5 * 255;
const b = (rem % 6) / 5 * 255;
return [r, g, b];
};
convert.rgb.hex = function (args) {
const integer = ((Math.round(args[0]) & 0xFF) << 16)
+ ((Math.round(args[1]) & 0xFF) << 8)
+ (Math.round(args[2]) & 0xFF);
const string = integer.toString(16).toUpperCase();
return '000000'.substring(string.length) + string;
};
convert.hex.rgb = function (args) {
const match = args.toString(16).match(/[a-f0-9]{6}|[a-f0-9]{3}/i);
if (!match) {
return [0, 0, 0];
}
let colorString = match[0];
if (match[0].length === 3) {
colorString = colorString.split('').map(char => {
return char + char;
}).join('');
}
const integer = parseInt(colorString, 16);
const r = (integer >> 16) & 0xFF;
const g = (integer >> 8) & 0xFF;
const b = integer & 0xFF;
return [r, g, b];
};
convert.rgb.hcg = function (rgb) {
const r = rgb[0] / 255;
const g = rgb[1] / 255;
const b = rgb[2] / 255;
const max = Math.max(Math.max(r, g), b);
const min = Math.min(Math.min(r, g), b);
const chroma = (max - min);
let grayscale;
let hue;
if (chroma < 1) {
grayscale = min / (1 - chroma);
} else {
grayscale = 0;
}
if (chroma <= 0) {
hue = 0;
} else
if (max === r) {
hue = ((g - b) / chroma) % 6;
} else
if (max === g) {
hue = 2 + (b - r) / chroma;
} else {
hue = 4 + (r - g) / chroma;
}
hue /= 6;
hue %= 1;
return [hue * 360, chroma * 100, grayscale * 100];
};
convert.hsl.hcg = function (hsl) {
const s = hsl[1] / 100;
const l = hsl[2] / 100;
const c = l < 0.5 ? (2.0 * s * l) : (2.0 * s * (1.0 - l));
let f = 0;
if (c < 1.0) {
f = (l - 0.5 * c) / (1.0 - c);
}
return [hsl[0], c * 100, f * 100];
};
convert.hsv.hcg = function (hsv) {
const s = hsv[1] / 100;
const v = hsv[2] / 100;
const c = s * v;
let f = 0;
if (c < 1.0) {
f = (v - c) / (1 - c);
}
return [hsv[0], c * 100, f * 100];
};
convert.hcg.rgb = function (hcg) {
const h = hcg[0] / 360;
const c = hcg[1] / 100;
const g = hcg[2] / 100;
if (c === 0.0) {
return [g * 255, g * 255, g * 255];
}
const pure = [0, 0, 0];
const hi = (h % 1) * 6;
const v = hi % 1;
const w = 1 - v;
let mg = 0;
/* eslint-disable max-statements-per-line */
switch (Math.floor(hi)) {
case 0:
pure[0] = 1; pure[1] = v; pure[2] = 0; break;
case 1:
pure[0] = w; pure[1] = 1; pure[2] = 0; break;
case 2:
pure[0] = 0; pure[1] = 1; pure[2] = v; break;
case 3:
pure[0] = 0; pure[1] = w; pure[2] = 1; break;
case 4:
pure[0] = v; pure[1] = 0; pure[2] = 1; break;
default:
pure[0] = 1; pure[1] = 0; pure[2] = w;
}
/* eslint-enable max-statements-per-line */
mg = (1.0 - c) * g;
return [
(c * pure[0] + mg) * 255,
(c * pure[1] + mg) * 255,
(c * pure[2] + mg) * 255
];
};
convert.hcg.hsv = function (hcg) {
const c = hcg[1] / 100;
const g = hcg[2] / 100;
const v = c + g * (1.0 - c);
let f = 0;
if (v > 0.0) {
f = c / v;
}
return [hcg[0], f * 100, v * 100];
};
convert.hcg.hsl = function (hcg) {
const c = hcg[1] / 100;
const g = hcg[2] / 100;
const l = g * (1.0 - c) + 0.5 * c;
let s = 0;
if (l > 0.0 && l < 0.5) {
s = c / (2 * l);
} else
if (l >= 0.5 && l < 1.0) {
s = c / (2 * (1 - l));
}
return [hcg[0], s * 100, l * 100];
};
convert.hcg.hwb = function (hcg) {
const c = hcg[1] / 100;
const g = hcg[2] / 100;
const v = c + g * (1.0 - c);
return [hcg[0], (v - c) * 100, (1 - v) * 100];
};
convert.hwb.hcg = function (hwb) {
const w = hwb[1] / 100;
const b = hwb[2] / 100;
const v = 1 - b;
const c = v - w;
let g = 0;
if (c < 1) {
g = (v - c) / (1 - c);
}
return [hwb[0], c * 100, g * 100];
};
convert.apple.rgb = function (apple) {
return [(apple[0] / 65535) * 255, (apple[1] / 65535) * 255, (apple[2] / 65535) * 255];
};
convert.rgb.apple = function (rgb) {
return [(rgb[0] / 255) * 65535, (rgb[1] / 255) * 65535, (rgb[2] / 255) * 65535];
};
convert.gray.rgb = function (args) {
return [args[0] / 100 * 255, args[0] / 100 * 255, args[0] / 100 * 255];
};
convert.gray.hsl = function (args) {
return [0, 0, args[0]];
};
convert.gray.hsv = convert.gray.hsl;
convert.gray.hwb = function (gray) {
return [0, 100, gray[0]];
};
convert.gray.cmyk = function (gray) {
return [0, 0, 0, gray[0]];
};
convert.gray.lab = function (gray) {
return [gray[0], 0, 0];
};
convert.gray.hex = function (gray) {
const val = Math.round(gray[0] / 100 * 255) & 0xFF;
const integer = (val << 16) + (val << 8) + val;
const string = integer.toString(16).toUpperCase();
return '000000'.substring(string.length) + string;
};
convert.rgb.gray = function (rgb) {
const val = (rgb[0] + rgb[1] + rgb[2]) / 3;
return [val / 255 * 100];
};

81
node_modules/karma/node_modules/color-convert/index.js generated vendored Normal file
View file

@ -0,0 +1,81 @@
const conversions = require('./conversions');
const route = require('./route');
const convert = {};
const models = Object.keys(conversions);
function wrapRaw(fn) {
const wrappedFn = function (...args) {
const arg0 = args[0];
if (arg0 === undefined || arg0 === null) {
return arg0;
}
if (arg0.length > 1) {
args = arg0;
}
return fn(args);
};
// Preserve .conversion property if there is one
if ('conversion' in fn) {
wrappedFn.conversion = fn.conversion;
}
return wrappedFn;
}
function wrapRounded(fn) {
const wrappedFn = function (...args) {
const arg0 = args[0];
if (arg0 === undefined || arg0 === null) {
return arg0;
}
if (arg0.length > 1) {
args = arg0;
}
const result = fn(args);
// We're assuming the result is an array here.
// see notice in conversions.js; don't use box types
// in conversion functions.
if (typeof result === 'object') {
for (let len = result.length, i = 0; i < len; i++) {
result[i] = Math.round(result[i]);
}
}
return result;
};
// Preserve .conversion property if there is one
if ('conversion' in fn) {
wrappedFn.conversion = fn.conversion;
}
return wrappedFn;
}
models.forEach(fromModel => {
convert[fromModel] = {};
Object.defineProperty(convert[fromModel], 'channels', {value: conversions[fromModel].channels});
Object.defineProperty(convert[fromModel], 'labels', {value: conversions[fromModel].labels});
const routes = route(fromModel);
const routeModels = Object.keys(routes);
routeModels.forEach(toModel => {
const fn = routes[toModel];
convert[fromModel][toModel] = wrapRounded(fn);
convert[fromModel][toModel].raw = wrapRaw(fn);
});
});
module.exports = convert;

View file

@ -0,0 +1,48 @@
{
"name": "color-convert",
"description": "Plain color conversion functions",
"version": "2.0.1",
"author": "Heather Arthur <fayearthur@gmail.com>",
"license": "MIT",
"repository": "Qix-/color-convert",
"scripts": {
"pretest": "xo",
"test": "node test/basic.js"
},
"engines": {
"node": ">=7.0.0"
},
"keywords": [
"color",
"colour",
"convert",
"converter",
"conversion",
"rgb",
"hsl",
"hsv",
"hwb",
"cmyk",
"ansi",
"ansi16"
],
"files": [
"index.js",
"conversions.js",
"route.js"
],
"xo": {
"rules": {
"default-case": 0,
"no-inline-comments": 0,
"operator-linebreak": 0
}
},
"devDependencies": {
"chalk": "^2.4.2",
"xo": "^0.24.0"
},
"dependencies": {
"color-name": "~1.1.4"
}
}

97
node_modules/karma/node_modules/color-convert/route.js generated vendored Normal file
View file

@ -0,0 +1,97 @@
const conversions = require('./conversions');
/*
This function routes a model to all other models.
all functions that are routed have a property `.conversion` attached
to the returned synthetic function. This property is an array
of strings, each with the steps in between the 'from' and 'to'
color models (inclusive).
conversions that are not possible simply are not included.
*/
function buildGraph() {
const graph = {};
// https://jsperf.com/object-keys-vs-for-in-with-closure/3
const models = Object.keys(conversions);
for (let len = models.length, i = 0; i < len; i++) {
graph[models[i]] = {
// http://jsperf.com/1-vs-infinity
// micro-opt, but this is simple.
distance: -1,
parent: null
};
}
return graph;
}
// https://en.wikipedia.org/wiki/Breadth-first_search
function deriveBFS(fromModel) {
const graph = buildGraph();
const queue = [fromModel]; // Unshift -> queue -> pop
graph[fromModel].distance = 0;
while (queue.length) {
const current = queue.pop();
const adjacents = Object.keys(conversions[current]);
for (let len = adjacents.length, i = 0; i < len; i++) {
const adjacent = adjacents[i];
const node = graph[adjacent];
if (node.distance === -1) {
node.distance = graph[current].distance + 1;
node.parent = current;
queue.unshift(adjacent);
}
}
}
return graph;
}
function link(from, to) {
return function (args) {
return to(from(args));
};
}
function wrapConversion(toModel, graph) {
const path = [graph[toModel].parent, toModel];
let fn = conversions[graph[toModel].parent][toModel];
let cur = graph[toModel].parent;
while (graph[cur].parent) {
path.unshift(graph[cur].parent);
fn = link(conversions[graph[cur].parent][cur], fn);
cur = graph[cur].parent;
}
fn.conversion = path;
return fn;
}
module.exports = function (fromModel) {
const graph = deriveBFS(fromModel);
const conversion = {};
const models = Object.keys(graph);
for (let len = models.length, i = 0; i < len; i++) {
const toModel = models[i];
const node = graph[toModel];
if (node.parent === null) {
// No possible conversion, or this node is the source model.
continue;
}
conversion[toModel] = wrapConversion(toModel, graph);
}
return conversion;
};

8
node_modules/karma/node_modules/color-name/LICENSE generated vendored Normal file
View file

@ -0,0 +1,8 @@
The MIT License (MIT)
Copyright (c) 2015 Dmitry Ivanov
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.

11
node_modules/karma/node_modules/color-name/README.md generated vendored Normal file
View file

@ -0,0 +1,11 @@
A JSON with color names and its values. Based on http://dev.w3.org/csswg/css-color/#named-colors.
[![NPM](https://nodei.co/npm/color-name.png?mini=true)](https://nodei.co/npm/color-name/)
```js
var colors = require('color-name');
colors.red //[255,0,0]
```
<a href="LICENSE"><img src="https://upload.wikimedia.org/wikipedia/commons/0/0c/MIT_logo.svg" width="120"/></a>

152
node_modules/karma/node_modules/color-name/index.js generated vendored Normal file
View file

@ -0,0 +1,152 @@
'use strict'
module.exports = {
"aliceblue": [240, 248, 255],
"antiquewhite": [250, 235, 215],
"aqua": [0, 255, 255],
"aquamarine": [127, 255, 212],
"azure": [240, 255, 255],
"beige": [245, 245, 220],
"bisque": [255, 228, 196],
"black": [0, 0, 0],
"blanchedalmond": [255, 235, 205],
"blue": [0, 0, 255],
"blueviolet": [138, 43, 226],
"brown": [165, 42, 42],
"burlywood": [222, 184, 135],
"cadetblue": [95, 158, 160],
"chartreuse": [127, 255, 0],
"chocolate": [210, 105, 30],
"coral": [255, 127, 80],
"cornflowerblue": [100, 149, 237],
"cornsilk": [255, 248, 220],
"crimson": [220, 20, 60],
"cyan": [0, 255, 255],
"darkblue": [0, 0, 139],
"darkcyan": [0, 139, 139],
"darkgoldenrod": [184, 134, 11],
"darkgray": [169, 169, 169],
"darkgreen": [0, 100, 0],
"darkgrey": [169, 169, 169],
"darkkhaki": [189, 183, 107],
"darkmagenta": [139, 0, 139],
"darkolivegreen": [85, 107, 47],
"darkorange": [255, 140, 0],
"darkorchid": [153, 50, 204],
"darkred": [139, 0, 0],
"darksalmon": [233, 150, 122],
"darkseagreen": [143, 188, 143],
"darkslateblue": [72, 61, 139],
"darkslategray": [47, 79, 79],
"darkslategrey": [47, 79, 79],
"darkturquoise": [0, 206, 209],
"darkviolet": [148, 0, 211],
"deeppink": [255, 20, 147],
"deepskyblue": [0, 191, 255],
"dimgray": [105, 105, 105],
"dimgrey": [105, 105, 105],
"dodgerblue": [30, 144, 255],
"firebrick": [178, 34, 34],
"floralwhite": [255, 250, 240],
"forestgreen": [34, 139, 34],
"fuchsia": [255, 0, 255],
"gainsboro": [220, 220, 220],
"ghostwhite": [248, 248, 255],
"gold": [255, 215, 0],
"goldenrod": [218, 165, 32],
"gray": [128, 128, 128],
"green": [0, 128, 0],
"greenyellow": [173, 255, 47],
"grey": [128, 128, 128],
"honeydew": [240, 255, 240],
"hotpink": [255, 105, 180],
"indianred": [205, 92, 92],
"indigo": [75, 0, 130],
"ivory": [255, 255, 240],
"khaki": [240, 230, 140],
"lavender": [230, 230, 250],
"lavenderblush": [255, 240, 245],
"lawngreen": [124, 252, 0],
"lemonchiffon": [255, 250, 205],
"lightblue": [173, 216, 230],
"lightcoral": [240, 128, 128],
"lightcyan": [224, 255, 255],
"lightgoldenrodyellow": [250, 250, 210],
"lightgray": [211, 211, 211],
"lightgreen": [144, 238, 144],
"lightgrey": [211, 211, 211],
"lightpink": [255, 182, 193],
"lightsalmon": [255, 160, 122],
"lightseagreen": [32, 178, 170],
"lightskyblue": [135, 206, 250],
"lightslategray": [119, 136, 153],
"lightslategrey": [119, 136, 153],
"lightsteelblue": [176, 196, 222],
"lightyellow": [255, 255, 224],
"lime": [0, 255, 0],
"limegreen": [50, 205, 50],
"linen": [250, 240, 230],
"magenta": [255, 0, 255],
"maroon": [128, 0, 0],
"mediumaquamarine": [102, 205, 170],
"mediumblue": [0, 0, 205],
"mediumorchid": [186, 85, 211],
"mediumpurple": [147, 112, 219],
"mediumseagreen": [60, 179, 113],
"mediumslateblue": [123, 104, 238],
"mediumspringgreen": [0, 250, 154],
"mediumturquoise": [72, 209, 204],
"mediumvioletred": [199, 21, 133],
"midnightblue": [25, 25, 112],
"mintcream": [245, 255, 250],
"mistyrose": [255, 228, 225],
"moccasin": [255, 228, 181],
"navajowhite": [255, 222, 173],
"navy": [0, 0, 128],
"oldlace": [253, 245, 230],
"olive": [128, 128, 0],
"olivedrab": [107, 142, 35],
"orange": [255, 165, 0],
"orangered": [255, 69, 0],
"orchid": [218, 112, 214],
"palegoldenrod": [238, 232, 170],
"palegreen": [152, 251, 152],
"paleturquoise": [175, 238, 238],
"palevioletred": [219, 112, 147],
"papayawhip": [255, 239, 213],
"peachpuff": [255, 218, 185],
"peru": [205, 133, 63],
"pink": [255, 192, 203],
"plum": [221, 160, 221],
"powderblue": [176, 224, 230],
"purple": [128, 0, 128],
"rebeccapurple": [102, 51, 153],
"red": [255, 0, 0],
"rosybrown": [188, 143, 143],
"royalblue": [65, 105, 225],
"saddlebrown": [139, 69, 19],
"salmon": [250, 128, 114],
"sandybrown": [244, 164, 96],
"seagreen": [46, 139, 87],
"seashell": [255, 245, 238],
"sienna": [160, 82, 45],
"silver": [192, 192, 192],
"skyblue": [135, 206, 235],
"slateblue": [106, 90, 205],
"slategray": [112, 128, 144],
"slategrey": [112, 128, 144],
"snow": [255, 250, 250],
"springgreen": [0, 255, 127],
"steelblue": [70, 130, 180],
"tan": [210, 180, 140],
"teal": [0, 128, 128],
"thistle": [216, 191, 216],
"tomato": [255, 99, 71],
"turquoise": [64, 224, 208],
"violet": [238, 130, 238],
"wheat": [245, 222, 179],
"white": [255, 255, 255],
"whitesmoke": [245, 245, 245],
"yellow": [255, 255, 0],
"yellowgreen": [154, 205, 50]
};

View file

@ -0,0 +1,28 @@
{
"name": "color-name",
"version": "1.1.4",
"description": "A list of color names and its values",
"main": "index.js",
"files": [
"index.js"
],
"scripts": {
"test": "node test.js"
},
"repository": {
"type": "git",
"url": "git@github.com:colorjs/color-name.git"
},
"keywords": [
"color-name",
"color",
"color-keyword",
"keyword"
],
"author": "DY <dfcreative@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://github.com/colorjs/color-name/issues"
},
"homepage": "https://github.com/colorjs/color-name"
}

21
node_modules/karma/node_modules/glob/LICENSE generated vendored Normal file
View file

@ -0,0 +1,21 @@
The ISC License
Copyright (c) Isaac Z. Schlueter and 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.
## Glob Logo
Glob's logo created by Tanya Brassie <http://tanyabrassie.com/>, licensed
under a Creative Commons Attribution-ShareAlike 4.0 International License
https://creativecommons.org/licenses/by-sa/4.0/

378
node_modules/karma/node_modules/glob/README.md generated vendored Normal file
View file

@ -0,0 +1,378 @@
# Glob
Match files using the patterns the shell uses, like stars and stuff.
[![Build Status](https://travis-ci.org/isaacs/node-glob.svg?branch=master)](https://travis-ci.org/isaacs/node-glob/) [![Build Status](https://ci.appveyor.com/api/projects/status/kd7f3yftf7unxlsx?svg=true)](https://ci.appveyor.com/project/isaacs/node-glob) [![Coverage Status](https://coveralls.io/repos/isaacs/node-glob/badge.svg?branch=master&service=github)](https://coveralls.io/github/isaacs/node-glob?branch=master)
This is a glob implementation in JavaScript. It uses the `minimatch`
library to do its matching.
![a fun cartoon logo made of glob characters](logo/glob.png)
## Usage
Install with npm
```
npm i glob
```
```javascript
var glob = require("glob")
// options is optional
glob("**/*.js", options, function (er, files) {
// files is an array of filenames.
// If the `nonull` option is set, and nothing
// was found, then files is ["**/*.js"]
// er is an error object or null.
})
```
## Glob Primer
"Globs" are the patterns you type when you do stuff like `ls *.js` on
the command line, or put `build/*` in a `.gitignore` file.
Before parsing the path part patterns, braced sections are expanded
into a set. Braced sections start with `{` and end with `}`, with any
number of comma-delimited sections within. Braced sections may contain
slash characters, so `a{/b/c,bcd}` would expand into `a/b/c` and `abcd`.
The following characters have special magic meaning when used in a
path portion:
* `*` Matches 0 or more characters in a single path portion
* `?` Matches 1 character
* `[...]` Matches a range of characters, similar to a RegExp range.
If the first character of the range is `!` or `^` then it matches
any character not in the range.
* `!(pattern|pattern|pattern)` Matches anything that does not match
any of the patterns provided.
* `?(pattern|pattern|pattern)` Matches zero or one occurrence of the
patterns provided.
* `+(pattern|pattern|pattern)` Matches one or more occurrences of the
patterns provided.
* `*(a|b|c)` Matches zero or more occurrences of the patterns provided
* `@(pattern|pat*|pat?erN)` Matches exactly one of the patterns
provided
* `**` If a "globstar" is alone in a path portion, then it matches
zero or more directories and subdirectories searching for matches.
It does not crawl symlinked directories.
### Dots
If a file or directory path portion has a `.` as the first character,
then it will not match any glob pattern unless that pattern's
corresponding path part also has a `.` as its first character.
For example, the pattern `a/.*/c` would match the file at `a/.b/c`.
However the pattern `a/*/c` would not, because `*` does not start with
a dot character.
You can make glob treat dots as normal characters by setting
`dot:true` in the options.
### Basename Matching
If you set `matchBase:true` in the options, and the pattern has no
slashes in it, then it will seek for any file anywhere in the tree
with a matching basename. For example, `*.js` would match
`test/simple/basic.js`.
### Empty Sets
If no matching files are found, then an empty array is returned. This
differs from the shell, where the pattern itself is returned. For
example:
$ echo a*s*d*f
a*s*d*f
To get the bash-style behavior, set the `nonull:true` in the options.
### See Also:
* `man sh`
* `man bash` (Search for "Pattern Matching")
* `man 3 fnmatch`
* `man 5 gitignore`
* [minimatch documentation](https://github.com/isaacs/minimatch)
## glob.hasMagic(pattern, [options])
Returns `true` if there are any special characters in the pattern, and
`false` otherwise.
Note that the options affect the results. If `noext:true` is set in
the options object, then `+(a|b)` will not be considered a magic
pattern. If the pattern has a brace expansion, like `a/{b/c,x/y}`
then that is considered magical, unless `nobrace:true` is set in the
options.
## glob(pattern, [options], cb)
* `pattern` `{String}` Pattern to be matched
* `options` `{Object}`
* `cb` `{Function}`
* `err` `{Error | null}`
* `matches` `{Array<String>}` filenames found matching the pattern
Perform an asynchronous glob search.
## glob.sync(pattern, [options])
* `pattern` `{String}` Pattern to be matched
* `options` `{Object}`
* return: `{Array<String>}` filenames found matching the pattern
Perform a synchronous glob search.
## Class: glob.Glob
Create a Glob object by instantiating the `glob.Glob` class.
```javascript
var Glob = require("glob").Glob
var mg = new Glob(pattern, options, cb)
```
It's an EventEmitter, and starts walking the filesystem to find matches
immediately.
### new glob.Glob(pattern, [options], [cb])
* `pattern` `{String}` pattern to search for
* `options` `{Object}`
* `cb` `{Function}` Called when an error occurs, or matches are found
* `err` `{Error | null}`
* `matches` `{Array<String>}` filenames found matching the pattern
Note that if the `sync` flag is set in the options, then matches will
be immediately available on the `g.found` member.
### Properties
* `minimatch` The minimatch object that the glob uses.
* `options` The options object passed in.
* `aborted` Boolean which is set to true when calling `abort()`. There
is no way at this time to continue a glob search after aborting, but
you can re-use the statCache to avoid having to duplicate syscalls.
* `cache` Convenience object. Each field has the following possible
values:
* `false` - Path does not exist
* `true` - Path exists
* `'FILE'` - Path exists, and is not a directory
* `'DIR'` - Path exists, and is a directory
* `[file, entries, ...]` - Path exists, is a directory, and the
array value is the results of `fs.readdir`
* `statCache` Cache of `fs.stat` results, to prevent statting the same
path multiple times.
* `symlinks` A record of which paths are symbolic links, which is
relevant in resolving `**` patterns.
* `realpathCache` An optional object which is passed to `fs.realpath`
to minimize unnecessary syscalls. It is stored on the instantiated
Glob object, and may be re-used.
### Events
* `end` When the matching is finished, this is emitted with all the
matches found. If the `nonull` option is set, and no match was found,
then the `matches` list contains the original pattern. The matches
are sorted, unless the `nosort` flag is set.
* `match` Every time a match is found, this is emitted with the specific
thing that matched. It is not deduplicated or resolved to a realpath.
* `error` Emitted when an unexpected error is encountered, or whenever
any fs error occurs if `options.strict` is set.
* `abort` When `abort()` is called, this event is raised.
### Methods
* `pause` Temporarily stop the search
* `resume` Resume the search
* `abort` Stop the search forever
### Options
All the options that can be passed to Minimatch can also be passed to
Glob to change pattern matching behavior. Also, some have been added,
or have glob-specific ramifications.
All options are false by default, unless otherwise noted.
All options are added to the Glob object, as well.
If you are running many `glob` operations, you can pass a Glob object
as the `options` argument to a subsequent operation to shortcut some
`stat` and `readdir` calls. At the very least, you may pass in shared
`symlinks`, `statCache`, `realpathCache`, and `cache` options, so that
parallel glob operations will be sped up by sharing information about
the filesystem.
* `cwd` The current working directory in which to search. Defaults
to `process.cwd()`.
* `root` The place where patterns starting with `/` will be mounted
onto. Defaults to `path.resolve(options.cwd, "/")` (`/` on Unix
systems, and `C:\` or some such on Windows.)
* `dot` Include `.dot` files in normal matches and `globstar` matches.
Note that an explicit dot in a portion of the pattern will always
match dot files.
* `nomount` By default, a pattern starting with a forward-slash will be
"mounted" onto the root setting, so that a valid filesystem path is
returned. Set this flag to disable that behavior.
* `mark` Add a `/` character to directory matches. Note that this
requires additional stat calls.
* `nosort` Don't sort the results.
* `stat` Set to true to stat *all* results. This reduces performance
somewhat, and is completely unnecessary, unless `readdir` is presumed
to be an untrustworthy indicator of file existence.
* `silent` When an unusual error is encountered when attempting to
read a directory, a warning will be printed to stderr. Set the
`silent` option to true to suppress these warnings.
* `strict` When an unusual error is encountered when attempting to
read a directory, the process will just continue on in search of
other matches. Set the `strict` option to raise an error in these
cases.
* `cache` See `cache` property above. Pass in a previously generated
cache object to save some fs calls.
* `statCache` A cache of results of filesystem information, to prevent
unnecessary stat calls. While it should not normally be necessary
to set this, you may pass the statCache from one glob() call to the
options object of another, if you know that the filesystem will not
change between calls. (See "Race Conditions" below.)
* `symlinks` A cache of known symbolic links. You may pass in a
previously generated `symlinks` object to save `lstat` calls when
resolving `**` matches.
* `sync` DEPRECATED: use `glob.sync(pattern, opts)` instead.
* `nounique` In some cases, brace-expanded patterns can result in the
same file showing up multiple times in the result set. By default,
this implementation prevents duplicates in the result set. Set this
flag to disable that behavior.
* `nonull` Set to never return an empty set, instead returning a set
containing the pattern itself. This is the default in glob(3).
* `debug` Set to enable debug logging in minimatch and glob.
* `nobrace` Do not expand `{a,b}` and `{1..3}` brace sets.
* `noglobstar` Do not match `**` against multiple filenames. (Ie,
treat it as a normal `*` instead.)
* `noext` Do not match `+(a|b)` "extglob" patterns.
* `nocase` Perform a case-insensitive match. Note: on
case-insensitive filesystems, non-magic patterns will match by
default, since `stat` and `readdir` will not raise errors.
* `matchBase` Perform a basename-only match if the pattern does not
contain any slash characters. That is, `*.js` would be treated as
equivalent to `**/*.js`, matching all js files in all directories.
* `nodir` Do not match directories, only files. (Note: to match
*only* directories, simply put a `/` at the end of the pattern.)
* `ignore` Add a pattern or an array of glob patterns to exclude matches.
Note: `ignore` patterns are *always* in `dot:true` mode, regardless
of any other settings.
* `follow` Follow symlinked directories when expanding `**` patterns.
Note that this can result in a lot of duplicate references in the
presence of cyclic links.
* `realpath` Set to true to call `fs.realpath` on all of the results.
In the case of a symlink that cannot be resolved, the full absolute
path to the matched entry is returned (though it will usually be a
broken symlink)
* `absolute` Set to true to always receive absolute paths for matched
files. Unlike `realpath`, this also affects the values returned in
the `match` event.
* `fs` File-system object with Node's `fs` API. By default, the built-in
`fs` module will be used. Set to a volume provided by a library like
`memfs` to avoid using the "real" file-system.
## Comparisons to other fnmatch/glob implementations
While strict compliance with the existing standards is a worthwhile
goal, some discrepancies exist between node-glob and other
implementations, and are intentional.
The double-star character `**` is supported by default, unless the
`noglobstar` flag is set. This is supported in the manner of bsdglob
and bash 4.3, where `**` only has special significance if it is the only
thing in a path part. That is, `a/**/b` will match `a/x/y/b`, but
`a/**b` will not.
Note that symlinked directories are not crawled as part of a `**`,
though their contents may match against subsequent portions of the
pattern. This prevents infinite loops and duplicates and the like.
If an escaped pattern has no matches, and the `nonull` flag is set,
then glob returns the pattern as-provided, rather than
interpreting the character escapes. For example,
`glob.match([], "\\*a\\?")` will return `"\\*a\\?"` rather than
`"*a?"`. This is akin to setting the `nullglob` option in bash, except
that it does not resolve escaped pattern characters.
If brace expansion is not disabled, then it is performed before any
other interpretation of the glob pattern. Thus, a pattern like
`+(a|{b),c)}`, which would not be valid in bash or zsh, is expanded
**first** into the set of `+(a|b)` and `+(a|c)`, and those patterns are
checked for validity. Since those two are valid, matching proceeds.
### Comments and Negation
Previously, this module let you mark a pattern as a "comment" if it
started with a `#` character, or a "negated" pattern if it started
with a `!` character.
These options were deprecated in version 5, and removed in version 6.
To specify things that should not match, use the `ignore` option.
## Windows
**Please only use forward-slashes in glob expressions.**
Though windows uses either `/` or `\` as its path separator, only `/`
characters are used by this glob implementation. You must use
forward-slashes **only** in glob expressions. Back-slashes will always
be interpreted as escape characters, not path separators.
Results from absolute patterns such as `/foo/*` are mounted onto the
root setting using `path.join`. On windows, this will by default result
in `/foo/*` matching `C:\foo\bar.txt`.
## Race Conditions
Glob searching, by its very nature, is susceptible to race conditions,
since it relies on directory walking and such.
As a result, it is possible that a file that exists when glob looks for
it may have been deleted or modified by the time it returns the result.
As part of its internal implementation, this program caches all stat
and readdir calls that it makes, in order to cut down on system
overhead. However, this also makes it even more susceptible to races,
especially if the cache or statCache objects are reused between glob
calls.
Users are thus advised not to use a glob result as a guarantee of
filesystem state in the face of rapid changes. For the vast majority
of operations, this is never a problem.
## Glob Logo
Glob's logo was created by [Tanya Brassie](http://tanyabrassie.com/). Logo files can be found [here](https://github.com/isaacs/node-glob/tree/master/logo).
The logo is licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License](https://creativecommons.org/licenses/by-sa/4.0/).
## Contributing
Any change to behavior (including bugfixes) must come with a test.
Patches that fail tests or reduce performance will be rejected.
```
# to run tests
npm test
# to re-generate test fixtures
npm run test-regen
# to benchmark against bash/zsh
npm run bench
# to profile javascript
npm run prof
```
![](oh-my-glob.gif)

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