first commit

This commit is contained in:
armansansd
2023-07-12 16:31:19 +01:00
commit 3424b1927b
23306 changed files with 2217776 additions and 0 deletions
+259
View File
@@ -0,0 +1,259 @@
# customizr
[![Build Status](https://api.travis-ci.org/Modernizr/customizr.png?branch=master,develop)](https://travis-ci.org/Modernizr/customizr)
[![NPM](https://nodei.co/npm/customizr.png?compact=true)](https://nodei.co/npm/customizr/)
##### *tl;dr:* This tool crawls through your project files, gathers up your references to Modernizr tests and outputs a lean, mean Modernizr machine.
`customizr` is a Modernizr builder for your project. It is based on the Modernizr team's [Modulizr](https://github.com/Modernizr/modernizr.com/blob/gh-pages/i/js/modulizr.js) tool.
This configurable task allows you to configure and export a custom Modernizr build. Use Modernizr's [annotated source](http://modernizr.com/downloads/modernizr-latest.js) for development, and let this tool worry about optimization.
When you're ready to build, `customizr` will crawl your project for Modernizr test references and save out a minified, uglified, customized version using only the tests you've used in your JavaScript or (S)CSS.
## Example
### CSS / SCSS / LESS
When going through css files, the crawler will not look for `display: flex` but rather if the code contains a css selector that is named like the Modernizr properties
```
.flexbox {
...
}
```
or
```
.no-flexbox {
...
}
```
### Javascript
When going through javascript files, the crawler will look for Modernizr calls like this one:
```
if (!Modernizr.flexbox) {
...
}
```
## Use with Grunt
A Grunt wrapper is available at: [https://github.com/Modernizr/grunt-modernizr](https://github.com/Modernizr/grunt-modernizr)
## Use with Gulp
A Gulp wrapper is available at: [https://github.com/rejas/gulp-modernizr](https://github.com/rejas/gulp-modernizr)
## Getting Started
Install with npm: `npm install --save customizr`
## Documentation
### Command Line
```
./node-modules/.bin/customizr -c path/to/config
```
### Command Line Options
```
-h, --help # Print options and usage
-v, --version # Print the version number
-c, --config # Path to your Modernizr config JSON file
-f, --force # Ignore cached versions and force build Modernizr
```
#### Config File
A sample config file is below. Default values shown:
```javascript
{
// Avoid unnecessary builds (see Caching section below)
"cache" : true,
// Path to the build you're using for development.
"devFile" : false,
// Path to save out the built file
"dest" : false,
// Based on default settings on http://modernizr.com/download/
"options" : [
"addTest",
"html5printshiv",
"testProp"
],
// By default, the build process is verbose
"quiet" : false,
// By default, source is uglified before saving
"uglify" : true,
// Define any tests you want to explicitly include
"tests" : [],
// Useful for excluding any tests that this tool will match
// e.g. you use .notification class for notification elements,
// but don’t want the test for Notification API
"excludeTests": [],
// By default, will crawl your project for references to Modernizr tests
// Set to false to disable
"crawl" : true,
// Set to true to pass in buffers via the "files" parameter below
"useBuffers" : false,
// By default, this task will crawl all *.js, *.css, *.scss files.
"files" : {
"src": [
"*[^(g|G)runt(file)?].{js,css,scss}",
"**[^node_modules]/**/*.{js,css,scss}",
"!lib/**/*"
]
},
// Have custom Modernizr tests? Add them here.
"customTests" : [],
// Add custom prefix to Modernizr CSS classes
"classPrefix" : ''
}
```
###### **`cache`** (Boolean, optional)
When true, `customizr` will avoid the expensive build process if a certain criteria is met (see [Caching](#caching) section below)
###### **`devFile`** (String, optional)
Path to the local build file you're using for development. This parameter is needed so `customizr` can skip your dev file when traversing your project to avoid triggering false positives. If you're using a remote file for development, set this option to `remote`.
This is an optional parameter. If you do not have a local devFile, set this option to `false`. Note that if this parameter is false and you have a local development file, it will find all Modernizr references from this file and will defeat the purpose of this tool.
###### **`dest`** (String, optional)
Path to save the customized Modernizr build. It defaults to `lib/modernizr-custom.js`.
This is an optional parameter. If undefined or falsy, `customizr` will return the result as a string and will not write to disk.
###### **`options`** (Array, optional)
An array of extra configuration options. Check the extra section on [modernizr.com/download](http://modernizr.com/download/) for complete options. Defaults are as they appear on the official site.
This is an optional parameter.
###### **`quiet`** (Boolean, optional)
By default, the build process is verbose. Set to true to build silently.
This is an optional parameter.
###### **`uglify`** (Boolean, optional)
By default, the source is uglified before save. Set to false to disable.
This is an optional parameter.
###### **`tests`** (Array, optional)
Define any tests you want to explicitly include. Check out the full set of test options [here](#ADD_LINK_LATER).
This is an optional parameter.
###### **`excludeTests`** (Array, optional)
Useful for excluding any tests that this tool will match. (e.g. you use .notification class for notification elements, but don’t want the test for Notification API).
This is an optional parameter.
###### **`crawl`** (Boolean, optional)
By default, this task will crawl your project for references to Modernizr tests. Set to false to disable.
This is an optional parameter.
###### **`useBuffers`** (Boolean, optional)
When `true`, the `files` parameter will accept an array of buffers in lieu of lookup strings.
###### **`files.src`** (Array, optional)
When `crawl` = `true`, this task will crawl all `*.js`, `*.css`, `*.scss` files. You can override this by defining a custom `files.src` array. The object supports either:
- An array of all [minimatch](https://github.com/isaacs/minimatch) options
- An array of [Vinyl-style](https://github.com/wearefractal/vinyl) File buffers. `useBuffers` must be `true` to enable this functionality.
This is an optional parameter.
###### **`customTests`** (Array, optional)
Have custom Modernizr tests? Add paths to their location here. The object supports all [minimatch](https://github.com/isaacs/minimatch) options.
This is an optional parameter.
###### **`classPrefix`** (String, optional)
Add custom prefix to Modernizr classes to avoid clashes with your preexisting class names.
This is an optional parameter.
## Caching
For large projects, building a custom Modernizr file can be an expensive task. `customizr` does its best to avoid unnecessary builds by following a set criteria. When all of the following are met, it assumes that no changes are necessary:
- If `customizr` has been previously run *AND*
- If [`settings.cache`](#cache-boolean-optional) is true *AND*
- If [`settings.dest`](#dest-string-optional) exists and is identical to the previous build *AND*
- If the `customizr` version is identical to the previous build *AND*
- If the `modernizr` dependency is identical to the previous build *AND*
- If the current [`customizr` settings](#config-file) are identical to the previous build *THEN*
`customizr` returns the cached data found in [`settings.dest`](#dest-string-optional)
- If any of the preceding rules are falsy, the cache is invalidated.
- If [`settings.cache`](#cache-boolean-optional) is falsy, the cache is invalidated.
- If [`settings.dest`](#dest-string-optional) is not defined, the cache is invalidated.
## Programmatic API
### require("customizr")(settings, callback)
- `settings` — A settings object as described above in "Config File".
- `callback` — A callback to execute when the task is finished
You can use `customizr` directly in your app if you prefer to not rely on the binary.
```js
var modernizr = require("customizr");
var settings = {
"cache" : true,
"devFile" : false,
"dest" : false,
"options" : [
"setClasses",
"addTest",
"html5printshiv",
"testProp"
],
"uglify" : true,
"tests" : [],
"excludeTests": [],
"crawl" : true,
"useBuffers": false,
"files" : {
"src": [
"*[^(g|G)runt(file)?].{js,css,scss}",
"**[^node_modules]/**/*.{js,css,scss}",
"!lib/**/*"
]
},
"customTests" : []
};
modernizr(settings, function () {
// all done!
});
```
## License
Copyright (c) 2013 Richard Herrera
Licensed under the MIT license.
+56
View File
@@ -0,0 +1,56 @@
#!/usr/bin/env node
"use strict";
var fs = require("fs"),
pkg = require("../package.json"),
nopt = require("nopt"),
path = require("path"),
customizr = require("../src");
var opts = nopt({
help: Boolean,
version: Boolean,
config: path,
force: Boolean
}, {
h: "--help",
v: "--version",
c: "--config",
f: "--force"
});
function help() {
var out = [
"Usage: customizr [options]",
"",
"General options:",
" -h, --help # Print options and usage",
" -v, --version # Print the version number",
" -c, --config # Path to your Modernizr config JSON file",
" -f, --force # Ignore cached versions and force build Modernizr",
""
];
return out.join("\n");
}
if (opts.help) {
process.stdout.write(help());
return process.exit();
} else if (opts.version) {
process.stdout.write(pkg.version + "\n");
return process.exit();
} else {
var config = {};
if (opts.config) {
if (fs.existsSync(opts.config)) {
config = JSON.parse(fs.readFileSync(opts.config));
} else {
process.stderr.write("Path does not exist.");
return process.exit();
}
}
return customizr(config, process.exit);
}
+6
View File
File diff suppressed because one or more lines are too long
+10013
View File
File diff suppressed because one or more lines are too long
+1554
View File
File diff suppressed because it is too large Load Diff
+1850
View File
File diff suppressed because it is too large Load Diff
+20
View File
@@ -0,0 +1,20 @@
{
"version": "1.2.0",
"modernizr": "^3",
"options": {
"feature-detects": [
"test/cors",
"test/input",
"test/svg/smil"
],
"options": [
"setClasses",
"addTest",
"html5printshiv",
"testProp"
],
"minify": false,
"dest": "build/modernizr-prefixed.js",
"classPrefix": "prefixed-"
}
}
+1
View File
@@ -0,0 +1 @@
../nopt/bin/nopt.js
+100
View File
@@ -0,0 +1,100 @@
# Change Log
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
<a name="6.0.5"></a>
## [6.0.5](https://github.com/moxystudio/node-cross-spawn/compare/v6.0.4...v6.0.5) (2018-03-02)
### Bug Fixes
* avoid using deprecated Buffer constructor ([#94](https://github.com/moxystudio/node-cross-spawn/issues/94)) ([d5770df](https://github.com/moxystudio/node-cross-spawn/commit/d5770df)), closes [/nodejs.org/api/deprecations.html#deprecations_dep0005](https://github.com//nodejs.org/api/deprecations.html/issues/deprecations_dep0005)
<a name="6.0.4"></a>
## [6.0.4](https://github.com/moxystudio/node-cross-spawn/compare/v6.0.3...v6.0.4) (2018-01-31)
### Bug Fixes
* fix paths being incorrectly normalized on unix ([06ee3c6](https://github.com/moxystudio/node-cross-spawn/commit/06ee3c6)), closes [#90](https://github.com/moxystudio/node-cross-spawn/issues/90)
<a name="6.0.3"></a>
## [6.0.3](https://github.com/moxystudio/node-cross-spawn/compare/v6.0.2...v6.0.3) (2018-01-23)
<a name="6.0.2"></a>
## [6.0.2](https://github.com/moxystudio/node-cross-spawn/compare/v6.0.1...v6.0.2) (2018-01-23)
<a name="6.0.1"></a>
## [6.0.1](https://github.com/moxystudio/node-cross-spawn/compare/v6.0.0...v6.0.1) (2018-01-23)
<a name="6.0.0"></a>
# [6.0.0](https://github.com/moxystudio/node-cross-spawn/compare/5.1.0...6.0.0) (2018-01-23)
### Bug Fixes
* fix certain arguments not being correctly escaped or causing batch syntax error ([900cf10](https://github.com/moxystudio/node-cross-spawn/commit/900cf10)), closes [#82](https://github.com/moxystudio/node-cross-spawn/issues/82) [#51](https://github.com/moxystudio/node-cross-spawn/issues/51)
* fix commands as posix relatixe paths not working correctly, e.g.: `./my-command` ([900cf10](https://github.com/moxystudio/node-cross-spawn/commit/900cf10))
* fix `options` argument being mutated ([900cf10](https://github.com/moxystudio/node-cross-spawn/commit/900cf10))
* fix commands resolution when PATH was actually Path ([900cf10](https://github.com/moxystudio/node-cross-spawn/commit/900cf10))
### Features
* improve compliance with node's ENOENT errors ([900cf10](https://github.com/moxystudio/node-cross-spawn/commit/900cf10))
* improve detection of node's shell option support ([900cf10](https://github.com/moxystudio/node-cross-spawn/commit/900cf10))
### Chores
* upgrade tooling
* upgrate project to es6 (node v4)
### BREAKING CHANGES
* remove support for older nodejs versions, only `node >= 4` is supported
<a name="5.1.0"></a>
## [5.1.0](https://github.com/moxystudio/node-cross-spawn/compare/5.0.1...5.1.0) (2017-02-26)
### Bug Fixes
* fix `options.shell` support for NodeJS [v4.8](https://github.com/nodejs/node/blob/master/doc/changelogs/CHANGELOG_V4.md#4.8.0)
<a name="5.0.1"></a>
## [5.0.1](https://github.com/moxystudio/node-cross-spawn/compare/5.0.0...5.0.1) (2016-11-04)
### Bug Fixes
* fix `options.shell` support for NodeJS v7
<a name="5.0.0"></a>
# [5.0.0](https://github.com/moxystudio/node-cross-spawn/compare/4.0.2...5.0.0) (2016-10-30)
## Features
* add support for `options.shell`
* improve parsing of shebangs by using [`shebang-command`](https://github.com/kevva/shebang-command) module
## Chores
* refactor some code to make it more clear
* update README caveats
+21
View File
@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2018 Made With MOXY Lda <hello@moxy.studio>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
+94
View File
@@ -0,0 +1,94 @@
# cross-spawn
[![NPM version][npm-image]][npm-url] [![Downloads][downloads-image]][npm-url] [![Build Status][travis-image]][travis-url] [![Build status][appveyor-image]][appveyor-url] [![Coverage Status][codecov-image]][codecov-url] [![Dependency status][david-dm-image]][david-dm-url] [![Dev Dependency status][david-dm-dev-image]][david-dm-dev-url] [![Greenkeeper badge][greenkeeper-image]][greenkeeper-url]
[npm-url]:https://npmjs.org/package/cross-spawn
[downloads-image]:http://img.shields.io/npm/dm/cross-spawn.svg
[npm-image]:http://img.shields.io/npm/v/cross-spawn.svg
[travis-url]:https://travis-ci.org/moxystudio/node-cross-spawn
[travis-image]:http://img.shields.io/travis/moxystudio/node-cross-spawn/master.svg
[appveyor-url]:https://ci.appveyor.com/project/satazor/node-cross-spawn
[appveyor-image]:https://img.shields.io/appveyor/ci/satazor/node-cross-spawn/master.svg
[codecov-url]:https://codecov.io/gh/moxystudio/node-cross-spawn
[codecov-image]:https://img.shields.io/codecov/c/github/moxystudio/node-cross-spawn/master.svg
[david-dm-url]:https://david-dm.org/moxystudio/node-cross-spawn
[david-dm-image]:https://img.shields.io/david/moxystudio/node-cross-spawn.svg
[david-dm-dev-url]:https://david-dm.org/moxystudio/node-cross-spawn?type=dev
[david-dm-dev-image]:https://img.shields.io/david/dev/moxystudio/node-cross-spawn.svg
[greenkeeper-image]:https://badges.greenkeeper.io/moxystudio/node-cross-spawn.svg
[greenkeeper-url]:https://greenkeeper.io/
A cross platform solution to node's spawn and spawnSync.
## Installation
`$ npm install cross-spawn`
## Why
Node has issues when using spawn on Windows:
- It ignores [PATHEXT](https://github.com/joyent/node/issues/2318)
- It does not support [shebangs](https://en.wikipedia.org/wiki/Shebang_(Unix))
- Has problems running commands with [spaces](https://github.com/nodejs/node/issues/7367)
- Has problems running commands with posix relative paths (e.g.: `./my-folder/my-executable`)
- Has an [issue](https://github.com/moxystudio/node-cross-spawn/issues/82) with command shims (files in `node_modules/.bin/`), where arguments with quotes and parenthesis would result in [invalid syntax error](https://github.com/moxystudio/node-cross-spawn/blob/e77b8f22a416db46b6196767bcd35601d7e11d54/test/index.test.js#L149)
- No `options.shell` support on node `<v4.8`
All these issues are handled correctly by `cross-spawn`.
There are some known modules, such as [win-spawn](https://github.com/ForbesLindesay/win-spawn), that try to solve this but they are either broken or provide faulty escaping of shell arguments.
## Usage
Exactly the same way as node's [`spawn`](https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options) or [`spawnSync`](https://nodejs.org/api/child_process.html#child_process_child_process_spawnsync_command_args_options), so it's a drop in replacement.
```js
const spawn = require('cross-spawn');
// Spawn NPM asynchronously
const child = spawn('npm', ['list', '-g', '-depth', '0'], { stdio: 'inherit' });
// Spawn NPM synchronously
const result = spawn.sync('npm', ['list', '-g', '-depth', '0'], { stdio: 'inherit' });
```
## Caveats
### Using `options.shell` as an alternative to `cross-spawn`
Starting from node `v4.8`, `spawn` has a `shell` option that allows you run commands from within a shell. This new option solves
the [PATHEXT](https://github.com/joyent/node/issues/2318) issue but:
- It's not supported in node `<v4.8`
- You must manually escape the command and arguments which is very error prone, specially when passing user input
- There are a lot of other unresolved issues from the [Why](#why) section that you must take into account
If you are using the `shell` option to spawn a command in a cross platform way, consider using `cross-spawn` instead. You have been warned.
### `options.shell` support
While `cross-spawn` adds support for `options.shell` in node `<v4.8`, all of its enhancements are disabled.
This mimics the Node.js behavior. More specifically, the command and its arguments will not be automatically escaped nor shebang support will be offered. This is by design because if you are using `options.shell` you are probably targeting a specific platform anyway and you don't want things to get into your way.
### Shebangs support
While `cross-spawn` handles shebangs on Windows, its support is limited. More specifically, it just supports `#!/usr/bin/env <program>` where `<program>` must not contain any arguments.
If you would like to have the shebang support improved, feel free to contribute via a pull-request.
Remember to always test your code on Windows!
## Tests
`$ npm test`
`$ npm test -- --watch` during development
## License
Released under the [MIT License](http://www.opensource.org/licenses/mit-license.php).
+39
View File
@@ -0,0 +1,39 @@
'use strict';
const cp = require('child_process');
const parse = require('./lib/parse');
const enoent = require('./lib/enoent');
function spawn(command, args, options) {
// Parse the arguments
const parsed = parse(command, args, options);
// Spawn the child process
const spawned = cp.spawn(parsed.command, parsed.args, parsed.options);
// Hook into child process "exit" event to emit an error if the command
// does not exists, see: https://github.com/IndigoUnited/node-cross-spawn/issues/16
enoent.hookChildProcess(spawned, parsed);
return spawned;
}
function spawnSync(command, args, options) {
// Parse the arguments
const parsed = parse(command, args, options);
// Spawn the child process
const result = cp.spawnSync(parsed.command, parsed.args, parsed.options);
// Analyze if the command does not exist, see: https://github.com/IndigoUnited/node-cross-spawn/issues/16
result.error = result.error || enoent.verifyENOENTSync(result.status, parsed);
return result;
}
module.exports = spawn;
module.exports.spawn = spawn;
module.exports.sync = spawnSync;
module.exports._parse = parse;
module.exports._enoent = enoent;
+59
View File
@@ -0,0 +1,59 @@
'use strict';
const isWin = process.platform === 'win32';
function notFoundError(original, syscall) {
return Object.assign(new Error(`${syscall} ${original.command} ENOENT`), {
code: 'ENOENT',
errno: 'ENOENT',
syscall: `${syscall} ${original.command}`,
path: original.command,
spawnargs: original.args,
});
}
function hookChildProcess(cp, parsed) {
if (!isWin) {
return;
}
const originalEmit = cp.emit;
cp.emit = function (name, arg1) {
// If emitting "exit" event and exit code is 1, we need to check if
// the command exists and emit an "error" instead
// See https://github.com/IndigoUnited/node-cross-spawn/issues/16
if (name === 'exit') {
const err = verifyENOENT(arg1, parsed, 'spawn');
if (err) {
return originalEmit.call(cp, 'error', err);
}
}
return originalEmit.apply(cp, arguments); // eslint-disable-line prefer-rest-params
};
}
function verifyENOENT(status, parsed) {
if (isWin && status === 1 && !parsed.file) {
return notFoundError(parsed.original, 'spawn');
}
return null;
}
function verifyENOENTSync(status, parsed) {
if (isWin && status === 1 && !parsed.file) {
return notFoundError(parsed.original, 'spawnSync');
}
return null;
}
module.exports = {
hookChildProcess,
verifyENOENT,
verifyENOENTSync,
notFoundError,
};
+125
View File
@@ -0,0 +1,125 @@
'use strict';
const path = require('path');
const niceTry = require('nice-try');
const resolveCommand = require('./util/resolveCommand');
const escape = require('./util/escape');
const readShebang = require('./util/readShebang');
const semver = require('semver');
const isWin = process.platform === 'win32';
const isExecutableRegExp = /\.(?:com|exe)$/i;
const isCmdShimRegExp = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i;
// `options.shell` is supported in Node ^4.8.0, ^5.7.0 and >= 6.0.0
const supportsShellOption = niceTry(() => semver.satisfies(process.version, '^4.8.0 || ^5.7.0 || >= 6.0.0', true)) || false;
function detectShebang(parsed) {
parsed.file = resolveCommand(parsed);
const shebang = parsed.file && readShebang(parsed.file);
if (shebang) {
parsed.args.unshift(parsed.file);
parsed.command = shebang;
return resolveCommand(parsed);
}
return parsed.file;
}
function parseNonShell(parsed) {
if (!isWin) {
return parsed;
}
// Detect & add support for shebangs
const commandFile = detectShebang(parsed);
// We don't need a shell if the command filename is an executable
const needsShell = !isExecutableRegExp.test(commandFile);
// If a shell is required, use cmd.exe and take care of escaping everything correctly
// Note that `forceShell` is an hidden option used only in tests
if (parsed.options.forceShell || needsShell) {
// Need to double escape meta chars if the command is a cmd-shim located in `node_modules/.bin/`
// The cmd-shim simply calls execute the package bin file with NodeJS, proxying any argument
// Because the escape of metachars with ^ gets interpreted when the cmd.exe is first called,
// we need to double escape them
const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile);
// Normalize posix paths into OS compatible paths (e.g.: foo/bar -> foo\bar)
// This is necessary otherwise it will always fail with ENOENT in those cases
parsed.command = path.normalize(parsed.command);
// Escape command & arguments
parsed.command = escape.command(parsed.command);
parsed.args = parsed.args.map((arg) => escape.argument(arg, needsDoubleEscapeMetaChars));
const shellCommand = [parsed.command].concat(parsed.args).join(' ');
parsed.args = ['/d', '/s', '/c', `"${shellCommand}"`];
parsed.command = process.env.comspec || 'cmd.exe';
parsed.options.windowsVerbatimArguments = true; // Tell node's spawn that the arguments are already escaped
}
return parsed;
}
function parseShell(parsed) {
// If node supports the shell option, there's no need to mimic its behavior
if (supportsShellOption) {
return parsed;
}
// Mimic node shell option
// See https://github.com/nodejs/node/blob/b9f6a2dc059a1062776133f3d4fd848c4da7d150/lib/child_process.js#L335
const shellCommand = [parsed.command].concat(parsed.args).join(' ');
if (isWin) {
parsed.command = typeof parsed.options.shell === 'string' ? parsed.options.shell : process.env.comspec || 'cmd.exe';
parsed.args = ['/d', '/s', '/c', `"${shellCommand}"`];
parsed.options.windowsVerbatimArguments = true; // Tell node's spawn that the arguments are already escaped
} else {
if (typeof parsed.options.shell === 'string') {
parsed.command = parsed.options.shell;
} else if (process.platform === 'android') {
parsed.command = '/system/bin/sh';
} else {
parsed.command = '/bin/sh';
}
parsed.args = ['-c', shellCommand];
}
return parsed;
}
function parse(command, args, options) {
// Normalize arguments, similar to nodejs
if (args && !Array.isArray(args)) {
options = args;
args = null;
}
args = args ? args.slice(0) : []; // Clone array to avoid changing the original
options = Object.assign({}, options); // Clone object to avoid changing the original
// Build our parsed object
const parsed = {
command,
args,
options,
file: undefined,
original: {
command,
args,
},
};
// Delegate further parsing to shell or non-shell
return options.shell ? parseShell(parsed) : parseNonShell(parsed);
}
module.exports = parse;
+45
View File
@@ -0,0 +1,45 @@
'use strict';
// See http://www.robvanderwoude.com/escapechars.php
const metaCharsRegExp = /([()\][%!^"`<>&|;, *?])/g;
function escapeCommand(arg) {
// Escape meta chars
arg = arg.replace(metaCharsRegExp, '^$1');
return arg;
}
function escapeArgument(arg, doubleEscapeMetaChars) {
// Convert to string
arg = `${arg}`;
// Algorithm below is based on https://qntm.org/cmd
// Sequence of backslashes followed by a double quote:
// double up all the backslashes and escape the double quote
arg = arg.replace(/(\\*)"/g, '$1$1\\"');
// Sequence of backslashes followed by the end of the string
// (which will become a double quote later):
// double up all the backslashes
arg = arg.replace(/(\\*)$/, '$1$1');
// All other backslashes occur literally
// Quote the whole thing:
arg = `"${arg}"`;
// Escape meta chars
arg = arg.replace(metaCharsRegExp, '^$1');
// Double escape meta chars if necessary
if (doubleEscapeMetaChars) {
arg = arg.replace(metaCharsRegExp, '^$1');
}
return arg;
}
module.exports.command = escapeCommand;
module.exports.argument = escapeArgument;
+32
View File
@@ -0,0 +1,32 @@
'use strict';
const fs = require('fs');
const shebangCommand = require('shebang-command');
function readShebang(command) {
// Read the first 150 bytes from the file
const size = 150;
let buffer;
if (Buffer.alloc) {
// Node.js v4.5+ / v5.10+
buffer = Buffer.alloc(size);
} else {
// Old Node.js API
buffer = new Buffer(size);
buffer.fill(0); // zero-fill
}
let fd;
try {
fd = fs.openSync(command, 'r');
fs.readSync(fd, buffer, 0, size, 0);
fs.closeSync(fd);
} catch (e) { /* Empty */ }
// Attempt to extract shebang (null is returned if not a shebang)
return shebangCommand(buffer.toString());
}
module.exports = readShebang;
@@ -0,0 +1,47 @@
'use strict';
const path = require('path');
const which = require('which');
const pathKey = require('path-key')();
function resolveCommandAttempt(parsed, withoutPathExt) {
const cwd = process.cwd();
const hasCustomCwd = parsed.options.cwd != null;
// If a custom `cwd` was specified, we need to change the process cwd
// because `which` will do stat calls but does not support a custom cwd
if (hasCustomCwd) {
try {
process.chdir(parsed.options.cwd);
} catch (err) {
/* Empty */
}
}
let resolved;
try {
resolved = which.sync(parsed.command, {
path: (parsed.options.env || process.env)[pathKey],
pathExt: withoutPathExt ? path.delimiter : undefined,
});
} catch (e) {
/* Empty */
} finally {
process.chdir(cwd);
}
// If we successfully resolved, ensure that an absolute path is returned
// Note that when a custom `cwd` was used, we need to resolve to an absolute path based on it
if (resolved) {
resolved = path.resolve(hasCustomCwd ? parsed.options.cwd : '', resolved);
}
return resolved;
}
function resolveCommand(parsed) {
return resolveCommandAttempt(parsed) || resolveCommandAttempt(parsed, true);
}
module.exports = resolveCommand;
+107
View File
@@ -0,0 +1,107 @@
{
"_from": "cross-spawn@^6.0.5",
"_id": "cross-spawn@6.0.5",
"_inBundle": false,
"_integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==",
"_location": "/customizr/cross-spawn",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "cross-spawn@^6.0.5",
"name": "cross-spawn",
"escapedName": "cross-spawn",
"rawSpec": "^6.0.5",
"saveSpec": null,
"fetchSpec": "^6.0.5"
},
"_requiredBy": [
"/customizr"
],
"_resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
"_shasum": "4a5ec7c64dfae22c3a14124dbacdee846d80cbc4",
"_spec": "cross-spawn@^6.0.5",
"_where": "/srv/http/mmap_sarrebourg/user/themes/basic/node_modules/customizr",
"author": {
"name": "André Cruz",
"email": "andre@moxy.studio"
},
"bugs": {
"url": "https://github.com/moxystudio/node-cross-spawn/issues"
},
"bundleDependencies": false,
"commitlint": {
"extends": [
"@commitlint/config-conventional"
]
},
"dependencies": {
"nice-try": "^1.0.4",
"path-key": "^2.0.1",
"semver": "^5.5.0",
"shebang-command": "^1.2.0",
"which": "^1.2.9"
},
"deprecated": false,
"description": "Cross platform child_process#spawn and child_process#spawnSync",
"devDependencies": {
"@commitlint/cli": "^6.0.0",
"@commitlint/config-conventional": "^6.0.2",
"babel-core": "^6.26.0",
"babel-jest": "^22.1.0",
"babel-preset-moxy": "^2.2.1",
"eslint": "^4.3.0",
"eslint-config-moxy": "^5.0.0",
"husky": "^0.14.3",
"jest": "^22.0.0",
"lint-staged": "^7.0.0",
"mkdirp": "^0.5.1",
"regenerator-runtime": "^0.11.1",
"rimraf": "^2.6.2",
"standard-version": "^4.2.0"
},
"engines": {
"node": ">=4.8"
},
"files": [
"lib"
],
"homepage": "https://github.com/moxystudio/node-cross-spawn",
"keywords": [
"spawn",
"spawnSync",
"windows",
"cross-platform",
"path-ext",
"shebang",
"cmd",
"execute"
],
"license": "MIT",
"lint-staged": {
"*.js": [
"eslint --fix",
"git add"
]
},
"main": "index.js",
"name": "cross-spawn",
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/moxystudio/node-cross-spawn.git"
},
"scripts": {
"commitmsg": "commitlint -e $GIT_PARAMS",
"lint": "eslint .",
"precommit": "lint-staged",
"prerelease": "npm t && npm run lint",
"release": "standard-version",
"test": "jest --env node --coverage"
},
"standard-version": {
"scripts": {
"posttag": "git push --follow-tags origin master && npm publish"
}
},
"version": "6.0.5"
}
+1
View File
@@ -0,0 +1 @@
node_modules
+8
View File
@@ -0,0 +1,8 @@
language: node_js
node_js:
- '0.12'
- '4'
- '6'
- '7'
before_install:
- npm install -g npm@latest
+58
View File
@@ -0,0 +1,58 @@
### v4.0.1 (2016-12-14)
#### WHOOPS
* [`fb9b1ce`](https://github.com/npm/nopt/commit/fb9b1ce57b3c69b4f7819015be87719204f77ef6)
Merged so many patches at once that the code fencing
([@adius](https://github.com/adius)) added got broken. Sorry,
([@adius](https://github.com/adius))!
([@othiym23](https://github.com/othiym23))
### v4.0.0 (2016-12-13)
#### BREAKING CHANGES
* [`651d447`](https://github.com/npm/nopt/commit/651d4473946096d341a480bbe56793de3fc706aa)
When parsing String-typed arguments, if the next value is `""`, don't simply
swallow it. ([@samjonester](https://github.com/samjonester))
#### PERFORMANCE TWEAKS
* [`3370ce8`](https://github.com/npm/nopt/commit/3370ce87a7618ba228883861db84ddbcdff252a9)
Simplify initialization. ([@elidoran](https://github.com/elidoran))
* [`356e58e`](https://github.com/npm/nopt/commit/356e58e3b3b431a4b1af7fd7bdee44c2c0526a09)
Store `Array.isArray(types[arg])` for reuse.
([@elidoran](https://github.com/elidoran))
* [`0d95e90`](https://github.com/npm/nopt/commit/0d95e90515844f266015b56d2c80b94e5d14a07e)
Interpret single-item type arrays as a single type.
([@samjonester](https://github.com/samjonester))
* [`07c69d3`](https://github.com/npm/nopt/commit/07c69d38b5186450941fbb505550becb78a0e925)
Simplify key-value extraction. ([@elidoran](https://github.com/elidoran))
* [`39b6e5c`](https://github.com/npm/nopt/commit/39b6e5c65ac47f60cd43a1fbeece5cd4c834c254)
Only call `Date.parse(val)` once. ([@elidoran](https://github.com/elidoran))
* [`934943d`](https://github.com/npm/nopt/commit/934943dffecb55123a2b15959fe2a359319a5dbd)
Use `osenv.home()` to find a user's home directory instead of assuming it's
always `$HOME`. ([@othiym23](https://github.com/othiym23))
#### TEST & CI IMPROVEMENTS
* [`326ffff`](https://github.com/npm/nopt/commit/326ffff7f78a00bcd316adecf69075f8a8093619)
Fix `/tmp` test to work on Windows.
([@elidoran](https://github.com/elidoran))
* [`c89d31a`](https://github.com/npm/nopt/commit/c89d31a49d14f2238bc6672db08da697bbc57f1b)
Only run Windows tests on Windows, only run Unix tests on a Unix.
([@elidoran](https://github.com/elidoran))
* [`affd3d1`](https://github.com/npm/nopt/commit/affd3d1d0addffa93006397b2013b18447339366)
Refresh Travis to run the tests against the currently-supported batch of npm
versions. ([@helio](https://github.com/helio)-frota)
* [`55f9449`](https://github.com/npm/nopt/commit/55f94497d163ed4d16dd55fd6c4fb95cc440e66d)
`tap@8.0.1` ([@othiym23](https://github.com/othiym23))
#### DOC TWEAKS
* [`5271229`](https://github.com/npm/nopt/commit/5271229ee7c810217dd51616c086f5d9ab224581)
Use JavaScript code block for syntax highlighting.
([@adius](https://github.com/adius))
* [`c0d156f`](https://github.com/npm/nopt/commit/c0d156f229f9994c5dfcec4a8886eceff7a07682)
The code sample in the README had `many2: [ oneThing ]`, and now it has
`many2: [ two, things ]`. ([@silkentrance](https://github.com/silkentrance))
+15
View File
@@ -0,0 +1,15 @@
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.
+213
View File
@@ -0,0 +1,213 @@
If you want to write an option parser, and have it be good, there are
two ways to do it. The Right Way, and the Wrong Way.
The Wrong Way is to sit down and write an option parser. We've all done
that.
The Right Way is to write some complex configurable program with so many
options that you hit the limit of your frustration just trying to
manage them all, and defer it with duct-tape solutions until you see
exactly to the core of the problem, and finally snap and write an
awesome option parser.
If you want to write an option parser, don't write an option parser.
Write a package manager, or a source control system, or a service
restarter, or an operating system. You probably won't end up with a
good one of those, but if you don't give up, and you are relentless and
diligent enough in your procrastination, you may just end up with a very
nice option parser.
## USAGE
```javascript
// my-program.js
var nopt = require("nopt")
, Stream = require("stream").Stream
, path = require("path")
, knownOpts = { "foo" : [String, null]
, "bar" : [Stream, Number]
, "baz" : path
, "bloo" : [ "big", "medium", "small" ]
, "flag" : Boolean
, "pick" : Boolean
, "many1" : [String, Array]
, "many2" : [path, Array]
}
, shortHands = { "foofoo" : ["--foo", "Mr. Foo"]
, "b7" : ["--bar", "7"]
, "m" : ["--bloo", "medium"]
, "p" : ["--pick"]
, "f" : ["--flag"]
}
// everything is optional.
// knownOpts and shorthands default to {}
// arg list defaults to process.argv
// slice defaults to 2
, parsed = nopt(knownOpts, shortHands, process.argv, 2)
console.log(parsed)
```
This would give you support for any of the following:
```console
$ node my-program.js --foo "blerp" --no-flag
{ "foo" : "blerp", "flag" : false }
$ node my-program.js ---bar 7 --foo "Mr. Hand" --flag
{ bar: 7, foo: "Mr. Hand", flag: true }
$ node my-program.js --foo "blerp" -f -----p
{ foo: "blerp", flag: true, pick: true }
$ node my-program.js -fp --foofoo
{ foo: "Mr. Foo", flag: true, pick: true }
$ node my-program.js --foofoo -- -fp # -- stops the flag parsing.
{ foo: "Mr. Foo", argv: { remain: ["-fp"] } }
$ node my-program.js --blatzk -fp # unknown opts are ok.
{ blatzk: true, flag: true, pick: true }
$ node my-program.js --blatzk=1000 -fp # but you need to use = if they have a value
{ blatzk: 1000, flag: true, pick: true }
$ node my-program.js --no-blatzk -fp # unless they start with "no-"
{ blatzk: false, flag: true, pick: true }
$ node my-program.js --baz b/a/z # known paths are resolved.
{ baz: "/Users/isaacs/b/a/z" }
# if Array is one of the types, then it can take many
# values, and will always be an array. The other types provided
# specify what types are allowed in the list.
$ node my-program.js --many1 5 --many1 null --many1 foo
{ many1: ["5", "null", "foo"] }
$ node my-program.js --many2 foo --many2 bar
{ many2: ["/path/to/foo", "path/to/bar"] }
```
Read the tests at the bottom of `lib/nopt.js` for more examples of
what this puppy can do.
## Types
The following types are supported, and defined on `nopt.typeDefs`
* String: A normal string. No parsing is done.
* path: A file system path. Gets resolved against cwd if not absolute.
* url: A url. If it doesn't parse, it isn't accepted.
* Number: Must be numeric.
* Date: Must parse as a date. If it does, and `Date` is one of the options,
then it will return a Date object, not a string.
* Boolean: Must be either `true` or `false`. If an option is a boolean,
then it does not need a value, and its presence will imply `true` as
the value. To negate boolean flags, do `--no-whatever` or `--whatever
false`
* NaN: Means that the option is strictly not allowed. Any value will
fail.
* Stream: An object matching the "Stream" class in node. Valuable
for use when validating programmatically. (npm uses this to let you
supply any WriteStream on the `outfd` and `logfd` config options.)
* Array: If `Array` is specified as one of the types, then the value
will be parsed as a list of options. This means that multiple values
can be specified, and that the value will always be an array.
If a type is an array of values not on this list, then those are
considered valid values. For instance, in the example above, the
`--bloo` option can only be one of `"big"`, `"medium"`, or `"small"`,
and any other value will be rejected.
When parsing unknown fields, `"true"`, `"false"`, and `"null"` will be
interpreted as their JavaScript equivalents.
You can also mix types and values, or multiple types, in a list. For
instance `{ blah: [Number, null] }` would allow a value to be set to
either a Number or null. When types are ordered, this implies a
preference, and the first type that can be used to properly interpret
the value will be used.
To define a new type, add it to `nopt.typeDefs`. Each item in that
hash is an object with a `type` member and a `validate` method. The
`type` member is an object that matches what goes in the type list. The
`validate` method is a function that gets called with `validate(data,
key, val)`. Validate methods should assign `data[key]` to the valid
value of `val` if it can be handled properly, or return boolean
`false` if it cannot.
You can also call `nopt.clean(data, types, typeDefs)` to clean up a
config object and remove its invalid properties.
## Error Handling
By default, nopt outputs a warning to standard error when invalid values for
known options are found. You can change this behavior by assigning a method
to `nopt.invalidHandler`. This method will be called with
the offending `nopt.invalidHandler(key, val, types)`.
If no `nopt.invalidHandler` is assigned, then it will console.error
its whining. If it is assigned to boolean `false` then the warning is
suppressed.
## Abbreviations
Yes, they are supported. If you define options like this:
```javascript
{ "foolhardyelephants" : Boolean
, "pileofmonkeys" : Boolean }
```
Then this will work:
```bash
node program.js --foolhar --pil
node program.js --no-f --pileofmon
# etc.
```
## Shorthands
Shorthands are a hash of shorter option names to a snippet of args that
they expand to.
If multiple one-character shorthands are all combined, and the
combination does not unambiguously match any other option or shorthand,
then they will be broken up into their constituent parts. For example:
```json
{ "s" : ["--loglevel", "silent"]
, "g" : "--global"
, "f" : "--force"
, "p" : "--parseable"
, "l" : "--long"
}
```
```bash
npm ls -sgflp
# just like doing this:
npm ls --loglevel silent --global --force --long --parseable
```
## The Rest of the args
The config object returned by nopt is given a special member called
`argv`, which is an object with the following fields:
* `remain`: The remaining args after all the parsing has occurred.
* `original`: The args as they originally appeared.
* `cooked`: The args after flags and shorthands are expanded.
## Slicing
Node programs are called with more or less the exact argv as it appears
in C land, after the v8 and node-specific options have been plucked off.
As such, `argv[0]` is always `node` and `argv[1]` is always the
JavaScript program being run.
That's usually not very useful to you. So they're sliced off by
default. If you want them, then you can pass in `0` as the last
argument, or any other number that you'd like to slice off the start of
the list.
+54
View File
@@ -0,0 +1,54 @@
#!/usr/bin/env node
var nopt = require("../lib/nopt")
, path = require("path")
, types = { num: Number
, bool: Boolean
, help: Boolean
, list: Array
, "num-list": [Number, Array]
, "str-list": [String, Array]
, "bool-list": [Boolean, Array]
, str: String
, clear: Boolean
, config: Boolean
, length: Number
, file: path
}
, shorthands = { s: [ "--str", "astring" ]
, b: [ "--bool" ]
, nb: [ "--no-bool" ]
, tft: [ "--bool-list", "--no-bool-list", "--bool-list", "true" ]
, "?": ["--help"]
, h: ["--help"]
, H: ["--help"]
, n: [ "--num", "125" ]
, c: ["--config"]
, l: ["--length"]
, f: ["--file"]
}
, parsed = nopt( types
, shorthands
, process.argv
, 2 )
console.log("parsed", parsed)
if (parsed.help) {
console.log("")
console.log("nopt cli tester")
console.log("")
console.log("types")
console.log(Object.keys(types).map(function M (t) {
var type = types[t]
if (Array.isArray(type)) {
return [t, type.map(function (type) { return type.name })]
}
return [t, type && type.name]
}).reduce(function (s, i) {
s[i[0]] = i[1]
return s
}, {}))
console.log("")
console.log("shorthands")
console.log(shorthands)
}
+30
View File
@@ -0,0 +1,30 @@
#!/usr/bin/env node
//process.env.DEBUG_NOPT = 1
// my-program.js
var nopt = require("../lib/nopt")
, Stream = require("stream").Stream
, path = require("path")
, knownOpts = { "foo" : [String, null]
, "bar" : [Stream, Number]
, "baz" : path
, "bloo" : [ "big", "medium", "small" ]
, "flag" : Boolean
, "pick" : Boolean
}
, shortHands = { "foofoo" : ["--foo", "Mr. Foo"]
, "b7" : ["--bar", "7"]
, "m" : ["--bloo", "medium"]
, "p" : ["--pick"]
, "f" : ["--flag", "true"]
, "g" : ["--flag"]
, "s" : "--flag"
}
// everything is optional.
// knownOpts and shorthands default to {}
// arg list defaults to process.argv
// slice defaults to 2
, parsed = nopt(knownOpts, shortHands, process.argv, 2)
console.log("parsed =\n"+ require("util").inspect(parsed))
+436
View File
@@ -0,0 +1,436 @@
// info about each config option.
var debug = process.env.DEBUG_NOPT || process.env.NOPT_DEBUG
? function () { console.error.apply(console, arguments) }
: function () {}
var url = require("url")
, path = require("path")
, Stream = require("stream").Stream
, abbrev = require("abbrev")
, osenv = require("osenv")
module.exports = exports = nopt
exports.clean = clean
exports.typeDefs =
{ String : { type: String, validate: validateString }
, Boolean : { type: Boolean, validate: validateBoolean }
, url : { type: url, validate: validateUrl }
, Number : { type: Number, validate: validateNumber }
, path : { type: path, validate: validatePath }
, Stream : { type: Stream, validate: validateStream }
, Date : { type: Date, validate: validateDate }
}
function nopt (types, shorthands, args, slice) {
args = args || process.argv
types = types || {}
shorthands = shorthands || {}
if (typeof slice !== "number") slice = 2
debug(types, shorthands, args, slice)
args = args.slice(slice)
var data = {}
, key
, argv = {
remain: [],
cooked: args,
original: args.slice(0)
}
parse(args, data, argv.remain, types, shorthands)
// now data is full
clean(data, types, exports.typeDefs)
data.argv = argv
Object.defineProperty(data.argv, 'toString', { value: function () {
return this.original.map(JSON.stringify).join(" ")
}, enumerable: false })
return data
}
function clean (data, types, typeDefs) {
typeDefs = typeDefs || exports.typeDefs
var remove = {}
, typeDefault = [false, true, null, String, Array]
Object.keys(data).forEach(function (k) {
if (k === "argv") return
var val = data[k]
, isArray = Array.isArray(val)
, type = types[k]
if (!isArray) val = [val]
if (!type) type = typeDefault
if (type === Array) type = typeDefault.concat(Array)
if (!Array.isArray(type)) type = [type]
debug("val=%j", val)
debug("types=", type)
val = val.map(function (val) {
// if it's an unknown value, then parse false/true/null/numbers/dates
if (typeof val === "string") {
debug("string %j", val)
val = val.trim()
if ((val === "null" && ~type.indexOf(null))
|| (val === "true" &&
(~type.indexOf(true) || ~type.indexOf(Boolean)))
|| (val === "false" &&
(~type.indexOf(false) || ~type.indexOf(Boolean)))) {
val = JSON.parse(val)
debug("jsonable %j", val)
} else if (~type.indexOf(Number) && !isNaN(val)) {
debug("convert to number", val)
val = +val
} else if (~type.indexOf(Date) && !isNaN(Date.parse(val))) {
debug("convert to date", val)
val = new Date(val)
}
}
if (!types.hasOwnProperty(k)) {
return val
}
// allow `--no-blah` to set 'blah' to null if null is allowed
if (val === false && ~type.indexOf(null) &&
!(~type.indexOf(false) || ~type.indexOf(Boolean))) {
val = null
}
var d = {}
d[k] = val
debug("prevalidated val", d, val, types[k])
if (!validate(d, k, val, types[k], typeDefs)) {
if (exports.invalidHandler) {
exports.invalidHandler(k, val, types[k], data)
} else if (exports.invalidHandler !== false) {
debug("invalid: "+k+"="+val, types[k])
}
return remove
}
debug("validated val", d, val, types[k])
return d[k]
}).filter(function (val) { return val !== remove })
if (!val.length) delete data[k]
else if (isArray) {
debug(isArray, data[k], val)
data[k] = val
} else data[k] = val[0]
debug("k=%s val=%j", k, val, data[k])
})
}
function validateString (data, k, val) {
data[k] = String(val)
}
function validatePath (data, k, val) {
if (val === true) return false
if (val === null) return true
val = String(val)
var isWin = process.platform === 'win32'
, homePattern = isWin ? /^~(\/|\\)/ : /^~\//
, home = osenv.home()
if (home && val.match(homePattern)) {
data[k] = path.resolve(home, val.substr(2))
} else {
data[k] = path.resolve(val)
}
return true
}
function validateNumber (data, k, val) {
debug("validate Number %j %j %j", k, val, isNaN(val))
if (isNaN(val)) return false
data[k] = +val
}
function validateDate (data, k, val) {
var s = Date.parse(val)
debug("validate Date %j %j %j", k, val, s)
if (isNaN(s)) return false
data[k] = new Date(val)
}
function validateBoolean (data, k, val) {
if (val instanceof Boolean) val = val.valueOf()
else if (typeof val === "string") {
if (!isNaN(val)) val = !!(+val)
else if (val === "null" || val === "false") val = false
else val = true
} else val = !!val
data[k] = val
}
function validateUrl (data, k, val) {
val = url.parse(String(val))
if (!val.host) return false
data[k] = val.href
}
function validateStream (data, k, val) {
if (!(val instanceof Stream)) return false
data[k] = val
}
function validate (data, k, val, type, typeDefs) {
// arrays are lists of types.
if (Array.isArray(type)) {
for (var i = 0, l = type.length; i < l; i ++) {
if (type[i] === Array) continue
if (validate(data, k, val, type[i], typeDefs)) return true
}
delete data[k]
return false
}
// an array of anything?
if (type === Array) return true
// NaN is poisonous. Means that something is not allowed.
if (type !== type) {
debug("Poison NaN", k, val, type)
delete data[k]
return false
}
// explicit list of values
if (val === type) {
debug("Explicitly allowed %j", val)
// if (isArray) (data[k] = data[k] || []).push(val)
// else data[k] = val
data[k] = val
return true
}
// now go through the list of typeDefs, validate against each one.
var ok = false
, types = Object.keys(typeDefs)
for (var i = 0, l = types.length; i < l; i ++) {
debug("test type %j %j %j", k, val, types[i])
var t = typeDefs[types[i]]
if (t &&
((type && type.name && t.type && t.type.name) ? (type.name === t.type.name) : (type === t.type))) {
var d = {}
ok = false !== t.validate(d, k, val)
val = d[k]
if (ok) {
// if (isArray) (data[k] = data[k] || []).push(val)
// else data[k] = val
data[k] = val
break
}
}
}
debug("OK? %j (%j %j %j)", ok, k, val, types[i])
if (!ok) delete data[k]
return ok
}
function parse (args, data, remain, types, shorthands) {
debug("parse", args, data, remain)
var key = null
, abbrevs = abbrev(Object.keys(types))
, shortAbbr = abbrev(Object.keys(shorthands))
for (var i = 0; i < args.length; i ++) {
var arg = args[i]
debug("arg", arg)
if (arg.match(/^-{2,}$/)) {
// done with keys.
// the rest are args.
remain.push.apply(remain, args.slice(i + 1))
args[i] = "--"
break
}
var hadEq = false
if (arg.charAt(0) === "-" && arg.length > 1) {
var at = arg.indexOf('=')
if (at > -1) {
hadEq = true
var v = arg.substr(at + 1)
arg = arg.substr(0, at)
args.splice(i, 1, arg, v)
}
// see if it's a shorthand
// if so, splice and back up to re-parse it.
var shRes = resolveShort(arg, shorthands, shortAbbr, abbrevs)
debug("arg=%j shRes=%j", arg, shRes)
if (shRes) {
debug(arg, shRes)
args.splice.apply(args, [i, 1].concat(shRes))
if (arg !== shRes[0]) {
i --
continue
}
}
arg = arg.replace(/^-+/, "")
var no = null
while (arg.toLowerCase().indexOf("no-") === 0) {
no = !no
arg = arg.substr(3)
}
if (abbrevs[arg]) arg = abbrevs[arg]
var argType = types[arg]
var isTypeArray = Array.isArray(argType)
if (isTypeArray && argType.length === 1) {
isTypeArray = false
argType = argType[0]
}
var isArray = argType === Array ||
isTypeArray && argType.indexOf(Array) !== -1
// allow unknown things to be arrays if specified multiple times.
if (!types.hasOwnProperty(arg) && data.hasOwnProperty(arg)) {
if (!Array.isArray(data[arg]))
data[arg] = [data[arg]]
isArray = true
}
var val
, la = args[i + 1]
var isBool = typeof no === 'boolean' ||
argType === Boolean ||
isTypeArray && argType.indexOf(Boolean) !== -1 ||
(typeof argType === 'undefined' && !hadEq) ||
(la === "false" &&
(argType === null ||
isTypeArray && ~argType.indexOf(null)))
if (isBool) {
// just set and move along
val = !no
// however, also support --bool true or --bool false
if (la === "true" || la === "false") {
val = JSON.parse(la)
la = null
if (no) val = !val
i ++
}
// also support "foo":[Boolean, "bar"] and "--foo bar"
if (isTypeArray && la) {
if (~argType.indexOf(la)) {
// an explicit type
val = la
i ++
} else if ( la === "null" && ~argType.indexOf(null) ) {
// null allowed
val = null
i ++
} else if ( !la.match(/^-{2,}[^-]/) &&
!isNaN(la) &&
~argType.indexOf(Number) ) {
// number
val = +la
i ++
} else if ( !la.match(/^-[^-]/) && ~argType.indexOf(String) ) {
// string
val = la
i ++
}
}
if (isArray) (data[arg] = data[arg] || []).push(val)
else data[arg] = val
continue
}
if (argType === String) {
if (la === undefined) {
la = ""
} else if (la.match(/^-{1,2}[^-]+/)) {
la = ""
i --
}
}
if (la && la.match(/^-{2,}$/)) {
la = undefined
i --
}
val = la === undefined ? true : la
if (isArray) (data[arg] = data[arg] || []).push(val)
else data[arg] = val
i ++
continue
}
remain.push(arg)
}
}
function resolveShort (arg, shorthands, shortAbbr, abbrevs) {
// handle single-char shorthands glommed together, like
// npm ls -glp, but only if there is one dash, and only if
// all of the chars are single-char shorthands, and it's
// not a match to some other abbrev.
arg = arg.replace(/^-+/, '')
// if it's an exact known option, then don't go any further
if (abbrevs[arg] === arg)
return null
// if it's an exact known shortopt, same deal
if (shorthands[arg]) {
// make it an array, if it's a list of words
if (shorthands[arg] && !Array.isArray(shorthands[arg]))
shorthands[arg] = shorthands[arg].split(/\s+/)
return shorthands[arg]
}
// first check to see if this arg is a set of single-char shorthands
var singles = shorthands.___singles
if (!singles) {
singles = Object.keys(shorthands).filter(function (s) {
return s.length === 1
}).reduce(function (l,r) {
l[r] = true
return l
}, {})
shorthands.___singles = singles
debug('shorthand singles', singles)
}
var chrs = arg.split("").filter(function (c) {
return singles[c]
})
if (chrs.join("") === arg) return chrs.map(function (c) {
return shorthands[c]
}).reduce(function (l, r) {
return l.concat(r)
}, [])
// if it's an arg abbrev, and not a literal shorthand, then prefer the arg
if (abbrevs[arg] && !shorthands[arg])
return null
// if it's an abbr for a shorthand, then use that
if (shortAbbr[arg])
arg = shortAbbr[arg]
// make it an array, if it's a list of words
if (shorthands[arg] && !Array.isArray(shorthands[arg]))
shorthands[arg] = shorthands[arg].split(/\s+/)
return shorthands[arg]
}
+58
View File
@@ -0,0 +1,58 @@
{
"_from": "nopt@^4.0.1",
"_id": "nopt@4.0.1",
"_inBundle": false,
"_integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=",
"_location": "/customizr/nopt",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "nopt@^4.0.1",
"name": "nopt",
"escapedName": "nopt",
"rawSpec": "^4.0.1",
"saveSpec": null,
"fetchSpec": "^4.0.1"
},
"_requiredBy": [
"/customizr"
],
"_resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz",
"_shasum": "d0d4685afd5415193c8c7505602d0d17cd64474d",
"_spec": "nopt@^4.0.1",
"_where": "/srv/http/mmap_sarrebourg/user/themes/basic/node_modules/customizr",
"author": {
"name": "Isaac Z. Schlueter",
"email": "i@izs.me",
"url": "http://blog.izs.me/"
},
"bin": {
"nopt": "./bin/nopt.js"
},
"bugs": {
"url": "https://github.com/npm/nopt/issues"
},
"bundleDependencies": false,
"dependencies": {
"abbrev": "1",
"osenv": "^0.1.4"
},
"deprecated": false,
"description": "Option parsing for Node, supporting types, shorthands, etc. Used by npm.",
"devDependencies": {
"tap": "^8.0.1"
},
"homepage": "https://github.com/npm/nopt#readme",
"license": "ISC",
"main": "lib/nopt.js",
"name": "nopt",
"repository": {
"type": "git",
"url": "git+https://github.com/npm/nopt.git"
},
"scripts": {
"test": "tap test/*.js"
},
"version": "4.0.1"
}
+303
View File
@@ -0,0 +1,303 @@
var nopt = require("../")
, test = require('tap').test
, isWin = process.platform === 'win32'
test("passing a string results in a string", function (t) {
var parsed = nopt({ key: String }, {}, ["--key", "myvalue"], 0)
t.same(parsed.key, "myvalue")
t.end()
})
// https://github.com/npm/nopt/issues/31
test("Empty String results in empty string, not true", function (t) {
var parsed = nopt({ empty: String }, {}, ["--empty"], 0)
t.same(parsed.empty, "")
t.end()
})
// https://github.com/npm/nopt/issues/65
test("Empty String should not swallow next flag", function (t) {
var parsed = nopt({ empty: String, foo: String }, {}, ["--empty", "--foo"], 0)
t.same(parsed.empty, "")
t.same(parsed.foo, "")
t.end()
})
// https://github.com/npm/nopt/issues/66
test("Empty String should not be true when type is single item Array", function (t) {
var parsed = nopt({ 'foo': [String] }, {}, ["--foo"], 0)
t.same(parsed.foo, "")
t.end()
})
test("~ path is resolved to " + (isWin ? '%USERPROFILE%' : '$HOME'), function (t) {
var path = require("path")
, the
if (isWin) {
the = {
key: 'USERPROFILE',
dir: 'C:\\temp',
val: '~\\val'
}
} else {
the = {
key: 'HOME',
dir: '/tmp',
val: '~/val'
}
}
if (!process.env[the.key]) process.env[the.key] = v.dir
var parsed = nopt({key: path}, {}, ["--key=" + the.val], 0)
t.same(parsed.key, path.resolve(process.env[the.key], "val"))
t.end()
})
// https://github.com/npm/nopt/issues/24
test("Unknown options are not parsed as numbers", function (t) {
var parsed = nopt({"parse-me": Number}, null, ['--leave-as-is=1.20', '--parse-me=1.20'], 0)
t.equal(parsed['leave-as-is'], '1.20')
t.equal(parsed['parse-me'], 1.2)
t.end()
});
// https://github.com/npm/nopt/issues/48
test("Check types based on name of type", function (t) {
var parsed = nopt({"parse-me": {name: "Number"}}, null, ['--parse-me=1.20'], 0)
t.equal(parsed['parse-me'], 1.2)
t.end()
})
test("Missing types are not parsed", function (t) {
var parsed = nopt({"parse-me": {}}, null, ['--parse-me=1.20'], 0)
//should only contain argv
t.equal(Object.keys(parsed).length, 1)
t.end()
})
test("Types passed without a name are not parsed", function (t) {
var parsed = nopt({"parse-me": {}}, {}, ['--parse-me=1.20'], 0)
//should only contain argv
t.equal(Object.keys(parsed).length, 1)
t.end()
})
test("other tests", function (t) {
var util = require("util")
, Stream = require("stream")
, path = require("path")
, url = require("url")
, shorthands =
{ s : ["--loglevel", "silent"]
, d : ["--loglevel", "info"]
, dd : ["--loglevel", "verbose"]
, ddd : ["--loglevel", "silly"]
, noreg : ["--no-registry"]
, reg : ["--registry"]
, "no-reg" : ["--no-registry"]
, silent : ["--loglevel", "silent"]
, verbose : ["--loglevel", "verbose"]
, h : ["--usage"]
, H : ["--usage"]
, "?" : ["--usage"]
, help : ["--usage"]
, v : ["--version"]
, f : ["--force"]
, desc : ["--description"]
, "no-desc" : ["--no-description"]
, "local" : ["--no-global"]
, l : ["--long"]
, p : ["--parseable"]
, porcelain : ["--parseable"]
, g : ["--global"]
}
, types =
{ aoa: Array
, nullstream: [null, Stream]
, date: Date
, str: String
, browser : String
, cache : path
, color : ["always", Boolean]
, depth : Number
, description : Boolean
, dev : Boolean
, editor : path
, force : Boolean
, global : Boolean
, globalconfig : path
, group : [String, Number]
, gzipbin : String
, logfd : [Number, Stream]
, loglevel : ["silent","win","error","warn","info","verbose","silly"]
, long : Boolean
, "node-version" : [false, String]
, npaturl : url
, npat : Boolean
, "onload-script" : [false, String]
, outfd : [Number, Stream]
, parseable : Boolean
, pre: Boolean
, prefix: path
, proxy : url
, "rebuild-bundle" : Boolean
, registry : url
, searchopts : String
, searchexclude: [null, String]
, shell : path
, t: [Array, String]
, tag : String
, tar : String
, tmp : path
, "unsafe-perm" : Boolean
, usage : Boolean
, user : String
, username : String
, userconfig : path
, version : Boolean
, viewer: path
, _exit : Boolean
, path: path
}
; [["-v", {version:true}, []]
,["---v", {version:true}, []]
,["ls -s --no-reg connect -d",
{loglevel:"info",registry:null},["ls","connect"]]
,["ls ---s foo",{loglevel:"silent"},["ls","foo"]]
,["ls --registry blargle", {}, ["ls"]]
,["--no-registry", {registry:null}, []]
,["--no-color true", {color:false}, []]
,["--no-color false", {color:true}, []]
,["--no-color", {color:false}, []]
,["--color false", {color:false}, []]
,["--color --logfd 7", {logfd:7,color:true}, []]
,["--color=true", {color:true}, []]
,["--logfd=10", {logfd:10}, []]
,["--tmp=/tmp -tar=gtar", {tmp: isWin ? "C:\\tmp" : "/tmp",tar:"gtar"},[]]
,["--tmp=tmp -tar=gtar",
{tmp:path.resolve(process.cwd(), "tmp"),tar:"gtar"},[]]
,["--logfd x", {}, []]
,["a -true -- -no-false", {true:true},["a","-no-false"]]
,["a -no-false", {false:false},["a"]]
,["a -no-no-true", {true:true}, ["a"]]
,["a -no-no-no-false", {false:false}, ["a"]]
,["---NO-no-No-no-no-no-nO-no-no"+
"-No-no-no-no-no-no-no-no-no"+
"-no-no-no-no-NO-NO-no-no-no-no-no-no"+
"-no-body-can-do-the-boogaloo-like-I-do"
,{"body-can-do-the-boogaloo-like-I-do":false}, []]
,["we are -no-strangers-to-love "+
"--you-know=the-rules --and=so-do-i "+
"---im-thinking-of=a-full-commitment "+
"--no-you-would-get-this-from-any-other-guy "+
"--no-gonna-give-you-up "+
"-no-gonna-let-you-down=true "+
"--no-no-gonna-run-around false "+
"--desert-you=false "+
"--make-you-cry false "+
"--no-tell-a-lie "+
"--no-no-and-hurt-you false"
,{"strangers-to-love":false
,"you-know":"the-rules"
,"and":"so-do-i"
,"you-would-get-this-from-any-other-guy":false
,"gonna-give-you-up":false
,"gonna-let-you-down":false
,"gonna-run-around":false
,"desert-you":false
,"make-you-cry":false
,"tell-a-lie":false
,"and-hurt-you":false
},["we", "are"]]
,["-t one -t two -t three"
,{t: ["one", "two", "three"]}
,[]]
,["-t one -t null -t three four five null"
,{t: ["one", "null", "three"]}
,["four", "five", "null"]]
,["-t foo"
,{t:["foo"]}
,[]]
,["--no-t"
,{t:["false"]}
,[]]
,["-no-no-t"
,{t:["true"]}
,[]]
,["-aoa one -aoa null -aoa 100"
,{aoa:["one", null, '100']}
,[]]
,["-str 100"
,{str:"100"}
,[]]
,["--color always"
,{color:"always"}
,[]]
,["--no-nullstream"
,{nullstream:null}
,[]]
,["--nullstream false"
,{nullstream:null}
,[]]
,["--notadate=2011-01-25"
,{notadate: "2011-01-25"}
,[]]
,["--date 2011-01-25"
,{date: new Date("2011-01-25")}
,[]]
,["-cl 1"
,{config: true, length: 1}
,[]
,{config: Boolean, length: Number, clear: Boolean}
,{c: "--config", l: "--length"}]
,["--acount bla"
,{"acount":true}
,["bla"]
,{account: Boolean, credentials: Boolean, options: String}
,{a:"--account", c:"--credentials",o:"--options"}]
,["--clear"
,{clear:true}
,[]
,{clear:Boolean,con:Boolean,len:Boolean,exp:Boolean,add:Boolean,rep:Boolean}
,{c:"--con",l:"--len",e:"--exp",a:"--add",r:"--rep"}]
,["--file -"
,{"file":"-"}
,[]
,{file:String}
,{}]
,["--file -"
,{"file":true}
,["-"]
,{file:Boolean}
,{}]
,["--path"
,{"path":null}
,[]]
,["--path ."
,{"path":process.cwd()}
,[]]
].forEach(function (test) {
var argv = test[0].split(/\s+/)
, opts = test[1]
, rem = test[2]
, actual = nopt(test[3] || types, test[4] || shorthands, argv, 0)
, parsed = actual.argv
delete actual.argv
for (var i in opts) {
var e = JSON.stringify(opts[i])
, a = JSON.stringify(actual[i] === undefined ? null : actual[i])
if (e && typeof e === "object") {
t.deepEqual(e, a)
} else {
t.equal(e, a)
}
}
t.deepEqual(rem, parsed.remain)
})
t.end()
})
+80
View File
@@ -0,0 +1,80 @@
{
"_from": "customizr@^1.2.1",
"_id": "customizr@1.2.1",
"_inBundle": false,
"_integrity": "sha512-ehwfABmC5eQQ3rGdvQnPUJEwolIvD3kYEQTM3+w6JH1SHaSRYKgnqLDv3+h030ZhmpoTyZlGmI66286LdILw5Q==",
"_location": "/customizr",
"_phantomChildren": {
"abbrev": "1.1.1",
"nice-try": "1.0.5",
"osenv": "0.1.5",
"path-key": "2.0.1",
"semver": "5.6.0",
"shebang-command": "1.2.0",
"which": "1.3.1"
},
"_requested": {
"type": "range",
"registry": true,
"raw": "customizr@^1.2.1",
"name": "customizr",
"escapedName": "customizr",
"rawSpec": "^1.2.1",
"saveSpec": null,
"fetchSpec": "^1.2.1"
},
"_requiredBy": [
"/gulp-modernizr"
],
"_resolved": "https://registry.npmjs.org/customizr/-/customizr-1.2.1.tgz",
"_shasum": "f6f246c708120558da8ab7038604f0f5bf9ee01b",
"_spec": "customizr@^1.2.1",
"_where": "/srv/http/mmap_sarrebourg/user/themes/basic/node_modules/gulp-modernizr",
"author": {
"name": "Richard Herrera"
},
"bin": {
"customizr": "bin/customizr"
},
"bugs": {
"url": "https://github.com/Modernizr/customizr/issues"
},
"bundleDependencies": false,
"dependencies": {
"colors": "^1.3.3",
"cross-spawn": "^6.0.5",
"fast-deep-equal": "^2.0.1",
"glob": "^7.1.3",
"lodash": "^4.17.11",
"mkdirp": "^0.5.1",
"modernizr": "^3",
"nopt": "^4.0.1",
"optimist": "^0.6.1",
"promised-io": "^0.3.5"
},
"deprecated": false,
"description": "Build out a lean, mean Modernizr machine.",
"devDependencies": {
"chai": "^4.2.0",
"fs-extra": "^7.0.1",
"mocha": "^6.0.2",
"nexpect": "^0.5.0"
},
"homepage": "https://github.com/Modernizr/customizr",
"keywords": [
"modernizr",
"customizr"
],
"license": "MIT",
"main": "src",
"name": "customizr",
"repository": {
"type": "git",
"url": "git+https://github.com/Modernizr/customizr.git"
},
"scripts": {
"pretest": "node test/pretest.js",
"test": "node test/runner.js"
},
"version": "1.2.1"
}
+118
View File
@@ -0,0 +1,118 @@
module.exports = function (modernizrPath) {
"use strict";
var argv = require("optimist").argv;
// Config object
var _force = argv.force;
// Deferreds
var promise = require("promised-io/promise");
// Cache utils
var utils;
return {
writeCodeToFile : function (result, config) {
utils.log.ok(("Success! Saved file to " + config.dest).grey);
return utils.file.write(config.dest, result);
},
init : function (tests) {
var deferred = new promise.Deferred(),
_interval;
// Cache utils
utils = this.utils;
// Store the current config
var settings = utils.getSettings();
// Check if we are minifying this build
var minify = settings.uglify;
// Store options
var options = settings.options;
var modernizrOptions = {
"feature-detects": tests,
"options": options,
"minify": minify,
"dest": settings.dest,
"classPrefix" : settings.classPrefix
};
// Perform a series of checks to validify cache
var useCachedVersion = false;
if (!_force) {
useCachedVersion = utils.checkCacheValidity(settings, modernizrOptions);
}
if (useCachedVersion) {
if(!settings.quiet) {
utils.log.writeln();
utils.log.writeln("No config or test changes detected".bold.white);
utils.log.ok("The build step has been bypassed. Use `--force` to override.".grey);
if (settings.dest) {
utils.log.ok(("Your current file can be found in " + settings.dest).grey);
}
}
setTimeout(function () {
return deferred.resolve({
result: useCachedVersion,
options: modernizrOptions
});
});
return deferred.promise;
}
// Echo settings
if(!settings.quiet) {
utils.log.writeln();
utils.log.ok("Ready to build using these settings:");
utils.log.ok(options.join(", ").grey);
if (minify) {
utils.log.ok("Your file will be minified with UglifyJS".grey);
}
utils.log.writeln();
utils.log.write("Building your customized Modernizr".bold.white);
_interval = setInterval(function () {
utils.log.write(".".grey);
}.bind(this), 200);
}
var modernizr = require("modernizr");
modernizr.build(modernizrOptions, function (result) {
if(!settings.quiet) {
utils.log.write("...".grey);
utils.log.ok();
clearInterval(_interval);
}
// Write code to file
if (settings.dest) {
this.builder.writeCodeToFile(result, settings);
}
return deferred.resolve({
result: result,
options: modernizrOptions
});
}.bind(this));
return deferred.promise;
}
};
};
+298
View File
@@ -0,0 +1,298 @@
module.exports = function (modernizrPath) {
"use strict";
var _ = require("lodash");
// Dependencies
var fs = require("fs"),
cwd = process.cwd(),
path = require("path");
// Deferreds
var promise = require("promised-io/promise");
// Cache utils
var utils, settings;
return {
matchedTestsInFile : {},
stringMatches : {},
filterTests : function (tests) {
var i, j, obj = {},
filteredTests = [];
for (i = 0, j = tests.length; i < j; i++) {
obj[tests[i]] = 0;
}
for (var key in obj) {
filteredTests.push(key);
}
return filteredTests;
},
findStringMatches : function (type, file, data, testpath) {
var match, regExp, prefix,
basename = path.basename(file);
// JavaScript/CoffeeScript/TypeScript files
if ((/\.(js|coffee|ts)$/).test(basename)) {
// Don't bother if we don't find a reference to Modernizr in the file...
if (!(/Modernizr/im).test(data)) {
return;
}
// Match usage such as: Modernizr.classname --or-- Modernizr['classname']
regExp = new RegExp("(?:\\.|\\[(?:\"|'))(" + type + ")(?![\\w-])(?:(?:\"|')\\])?", "gm");
}
// If it's not script, assume it's CSS (or similar, e.g.: LESS, SCSS) files
else {
prefix = settings.classPrefix || '';
// When no prefix, match usage such as: .classname --or-- .no-classname
// When prefix set, match usage such as: .<prefix>classname --or-- .<prefix>no-classname
regExp = new RegExp("(?:\\." + prefix + ")(?:no-)?(" + type + ")(?![\\w-])", "gm");
}
match = (regExp).exec(data);
this.matchedTestsInFile[file] = this.matchedTestsInFile[file] || [];
while (match) {
var test = match[1];
if (test) {
this.stringMatches[testpath] = this.stringMatches[testpath] || [];
if (this.stringMatches[testpath].indexOf(file) === -1) {
this.stringMatches[testpath].push(file);
}
if (this.matchedTestsInFile[file].indexOf(test) === -1) {
this.matchedTestsInFile[file].push(test);
}
}
match = (regExp).exec(data);
}
},
parseData : function (file, data, metadata) {
data = data.toString();
var testpath, test, type, value, i, j;
for (test in metadata) {
for (type in metadata[test]) {
if (type === "name" || type === "path") {
continue;
}
value = metadata[test][type];
testpath = metadata[test].path;
if (Array.isArray(value)) {
for (i = 0, j = value.length; i < j; i++) {
this.findStringMatches(value[i], file, data, testpath);
}
} else {
this.findStringMatches(value, file, data, testpath);
}
}
}
var matchedTests = this.matchedTestsInFile[file];
if (!settings.quiet && matchedTests && matchedTests.length) {
utils.log.writeln();
var testCount = matchedTests.length;
var testText = " match" + (testCount > 1 ? "es" : "") + " in ";
utils.log.ok(testCount.toString().green + testText + file);
utils.log.ok(matchedTests.sort().join(", ").grey);
}
},
readFile : function (file, metadata, deferred) {
var stream = fs.createReadStream(file);
stream.on("data", function (data) {
this.parseData(file, data, metadata);
}.bind(this));
stream.on("end", function () {
if ((++this.currentFile) === this.totalFiles) {
return deferred.resolve();
}
}.bind(this));
},
readFilesAsync : function (files, metadata) {
var deferred = new promise.Deferred(),
i, j, last;
this.currentFile = 0;
this.totalFiles = files.length;
for (i = 0, j = files.length; i < j; i++) {
this.readFile(files[i], metadata, deferred);
}
return deferred.promise;
},
parseCodeFromBuffers : function (buffers, metadata) {
var i, j, file;
for (i = 0, j = buffers.length; i < j; i++) {
file = buffers[i];
this.parseData(file.path, String(file.contents), metadata);
}
},
init : function (metadata) {
// Cache utils
utils = this.utils;
settings = utils.getSettings();
var _private = utils.getDefaults().private;
var deferred = new promise.Deferred(),
buildPath = path.join(modernizrPath, "build"),
files;
var tests = settings.tests.map(function (test) {
var data = metadata.filter(function (data) {
if (Array.isArray(test)) {
return test.indexOf(data) !== -1 || _.isEqual(test, data.property);
}
return data.property === test;
});
return data[0] || {};
}).filter(function (test) {
return test.path;
});
if (!settings.quiet && tests && tests.length) {
utils.log.writeln();
utils.log.ok("Explicitly including these tests:");
utils.log.ok(tests.map(function (test) {
return test.property;
}).sort().join(", ").grey);
}
var excludedTests = settings.excludeTests.map(function (test) {
var data = metadata.filter(function (data) {
if (Array.isArray(data.property)) {
return data.property.indexOf(test) !== -1;
} else {
return data.property === test;
}
});
return data[0] || {};
}).filter(function (test) {
return test.path;
});
if (!settings.quiet && excludedTests && excludedTests.length) {
utils.log.writeln();
utils.log.ok("Explicitly excluding these tests:");
utils.log.ok(excludedTests.map(function (test) {
return test.property;
}).sort().join(", ").grey);
}
tests = tests.map(function (test) {
return test.path;
}).filter(function (test) {
return excludedTests.map(function (test) {
return test.path;
}).indexOf(test) === -1;
}).concat(settings.customTests.map(function (test) {
return path.relative(buildPath, fs.realpathSync(test));
}));
metadata = metadata.filter(function (data) {
return excludedTests.map(function (test) {
return test.path;
}).indexOf(data.path) === -1;
});
if (settings.crawl !== true && settings.useBuffers !== true) {
tests = this.crawler.filterTests(tests);
if (!settings.quiet) {
utils.log.subhead("Skipping file traversal");
}
setTimeout(function () {
return deferred.resolve(tests);
}, 0);
return deferred.promise;
}
if (!settings.quiet) {
utils.log.subhead("Looking for Modernizr references");
}
// Support including code via string rather than reading a file.
if (settings.useBuffers === true) {
this.crawler.parseCodeFromBuffers(settings.files.src, metadata);
for (var key in this.crawler.stringMatches) {
tests.push(key);
}
tests = this.crawler.filterTests(tests);
setTimeout(function () {
return deferred.resolve(tests);
});
return deferred.promise;
}
// Exclude developer build
if (settings.devFile !== "remote" && settings.devFile !== false) {
if (!fs.existsSync(settings.devFile)) {
utils.log.warn([
"Can't find your Modernizr development build at " + settings.devFile,
"this tool needs this path to avoid false positives",
"",
"Update your settings via the modernizr.devFile config option",
"See %s#devfile-string for more details".replace("%s", _private.url.github),
"",
""
].join("\n ").replace(/\s$/, ""));
} else {
settings.files.src.push("!" + settings.devFile);
}
}
// Exclude generated file
if (settings.dest) {
settings.files.src.push("!" + settings.dest);
}
// And exclude all files in this current directory
settings.files.src.push("!" + path.join(__dirname.replace(cwd + path.sep, ""), "**", "*"));
files = utils.file.expand({
filter: "isFile"
}, settings.files.src);
this.crawler.readFilesAsync(files, metadata).then(function () {
for (var key in this.crawler.stringMatches) {
tests.push(key);
}
tests = this.crawler.filterTests(tests);
return deferred.resolve(tests);
}.bind(this));
return deferred.promise;
}
};
};
+64
View File
@@ -0,0 +1,64 @@
module.exports = function (settings, callback) {
"use strict";
// Dependencies
var fs = require("fs"),
path = require("path");
// Deferreds
var promise = require("promised-io/promise");
// Modernizr
var modernizrPath = path.join(__dirname, "..", "node_modules", "modernizr");
var Customizr = function () {
return this.init();
};
Customizr.prototype = {
init : function () {
// Store settings
this.utils.storeSettings(settings);
// Sequentially return promises
promise.seq([
// Use Modernizr to fetch metadata from each feature detect
this.metadata.init.bind(this),
// Look in the current project for references to tests
this.crawler.init.bind(this),
// Construct a list with matching positives, tell Modernizr to build a custom suite
this.builder.init.bind(this),
// Send done callback
this.finalize.bind(this)
]);
},
finalize : function (obj) {
// Store the current options.
// If a subsequent build matches the options,
// we can assume the cached version will do fine.
this.utils.saveOptions(obj.options);
// All done.
return (callback || function () {})(obj);
}
};
// Import modules
fs.readdirSync(__dirname).filter(function (file) {
return path.extname(file) === ".js";
}).filter(function (file) {
return file !== path.basename(__filename);
}).forEach(function (file) {
var _import = path.basename(file, ".js");
Customizr.prototype[_import] = require(path.join(__dirname, _import))(modernizrPath);
});
return new Customizr();
};
+33
View File
@@ -0,0 +1,33 @@
module.exports = function (modernizrPath) {
"use strict";
// Deferreds
var promise = require("promised-io/promise");
return {
init : function () {
var deferred = new promise.Deferred();
var modernizr = require("modernizr");
modernizr.metadata(function (metadata) {
var modRegExp = new RegExp(modernizrPath + "/?");
var mappings = metadata.map(function (map) {
var cleanname = map.name.replace(modRegExp, ""),
testpath = map.amdPath.replace(modRegExp, "").replace("feature-detects", "test");
return {
"path": testpath.replace(".js", ""),
"name": cleanname,
"property": map.property,
"cssclass": map.cssclass
};
});
return deferred.resolve(mappings);
});
return deferred.promise;
}
};
};
+34
View File
@@ -0,0 +1,34 @@
{
"defaults": {
"cache" : true,
"devFile" : false,
"dest" : false,
"options" : [
"setClasses",
"addTest",
"html5printshiv",
"testProp"
],
"quiet": false,
"uglify" : true,
"tests" : [],
"excludeTests": [],
"crawl" : true,
"useBuffers": false,
"files" : {
"src": [
"*[^(g|G)runt(file)?].{js,css,scss}",
"**[^node_modules]/**/*.{js,css,scss}",
"!lib/**/*"
]
},
"customTests" : [],
"classPrefix" : ""
},
"private": {
"url" : {
"github" : "https://github.com/Modernizr/grunt-modernizr",
"domain" : "http://modernizr.com"
}
}
}
+226
View File
@@ -0,0 +1,226 @@
module.exports = function (modernizrPath) {
"use strict";
// Dependencies
var fs = require("fs"),
path = require("path"),
glob = require("glob"),
colors = require("colors"),
equal = require("fast-deep-equal"),
mkdirp = require("mkdirp"),
_ = require("lodash");
// Lovingly lifted verbatim from Grunt: https://github.com/gruntjs/grunt/blob/master/lib/grunt/file.js
//
// Process specified wildcard glob patterns or filenames against a
// callback, excluding and uniquing files in the result set.
var processPatterns = function (patterns, fn) {
// Filepaths to return.
var result = [];
// Iterate over flattened patterns array.
_.flatten(patterns).forEach(function (pattern) {
// If the first character is ! it should be omitted
var exclusion = pattern.indexOf('!') === 0;
// If the pattern is an exclusion, remove the !
if (exclusion) {
pattern = pattern.slice(1);
}
// Find all matching files for this pattern.
var matches = fn(pattern);
if (exclusion) {
// If an exclusion, remove matching files.
result = _.difference(result, matches);
} else {
// Otherwise add matching files.
result = _.union(result, matches);
}
});
return result;
};
return {
_ : require("lodash"),
file : {
delete : function (filepath) {
return fs.unlinkSync(filepath);
},
exists : function (filepath) {
return fs.existsSync(filepath);
},
// Return an array of all file paths that match the given wildcard patterns.
expand : function (options, patterns) {
// Return all matching filepaths.
var matches = processPatterns(patterns, function (pattern) {
// Find all matching files for this pattern.
return glob.sync(pattern, options);
});
// Filter result set?
if (options.filter) {
matches = matches.filter(function (filepath) {
filepath = path.join(options.cwd || '', filepath);
try {
if (typeof options.filter === 'function') {
return options.filter(filepath);
} else {
// If the file is of the right type and exists, this should work.
return fs.statSync(filepath)[options.filter]();
}
} catch (e) {
// Otherwise, it's probably not the right type.
return false;
}
});
}
return matches;
},
mkdir : function (filepath) {
return mkdirp.sync(path.dirname(filepath));
},
read : function (filepath) {
return String(fs.readFileSync(filepath));
},
readJSON : function (filepath) {
return JSON.parse(fs.readFileSync(filepath));
},
write : function (filepath, string, options) {
mkdirp.sync(path.dirname(filepath));
if (fs.existsSync(filepath)) {
fs.unlinkSync(filepath);
}
return fs.writeFileSync(filepath, string, options);
}
},
log : {
ok : function (string) {
if (string) {
return process.stdout.write(">> ".green + string + "\n");
}
return process.stdout.write("OK".green + "\n");
},
subhead : function (string) {
string = string || "";
return process.stdout.write("\n" + string.bold + "\n");
},
write : function (string) {
string = string || "";
return process.stdout.write(string);
},
warn : function (string) {
string = string || "";
return process.stdout.write(("Warning: " + string + " \n").yellow);
},
writeln : function (string) {
string = string || "";
return process.stdout.write(string + "\n");
}
},
checkCacheValidity : function (currentConfig, modernizrOptions) {
var jsonPath = path.join(__dirname, "..", "package.json");
var pkg = this.file.readJSON(jsonPath);
// Check if a previous config exists
var previous = this.getPreviousOptions();
if (
currentConfig.cache === true &&
(currentConfig.dest && this.file.exists(currentConfig.dest)) &&
pkg &&
previous &&
previous.version === pkg.version &&
previous.modernizr === pkg.dependencies.modernizr &&
equal(previous.options, modernizrOptions)
) {
return this.file.read(currentConfig.dest);
}
return false;
},
getSettings : function () {
return this.settings || {};
},
storeSettings : function (settings) {
var _defaults = this._.clone(this.getDefaults().defaults);
settings = this._.extend(_defaults, settings);
this.settings = settings;
return settings;
},
getPreviousOptions : function () {
var cacheDir = path.join(__dirname, "..", "cache");
var optionsLocation = path.join(cacheDir, "options.json");
// Check if cache directory doesn't exist
if (!this.file.exists(optionsLocation)) {
return false;
}
// Otherwise, return the options.
return this.file.readJSON(optionsLocation);
},
saveOptions : function (options) {
// If no options parameter, assume no further action.
if (typeof options === "undefined") {
return;
}
var cacheDir = path.join(__dirname, "..", "cache");
var optionsLocation = path.join(cacheDir, "options.json");
// Check if cache directory doesn't exist
if (!this.file.exists(cacheDir)) {
this.file.mkdir(cacheDir);
}
// Remove old options
if (this.file.exists(optionsLocation)) {
this.file.delete(optionsLocation);
}
var jsonPath = path.join(__dirname, "..", "package.json");
var pkg = this.file.readJSON(jsonPath);
// Stash options along with metadata
var metadata = {
version: pkg.version,
modernizr: (pkg.dependencies || {}).modernizr,
options: options
};
// Write new options
this.file.write(optionsLocation, JSON.stringify(metadata, null, 2));
},
getDefaults : function () {
this.defaults = this.defaults || this.file.readJSON(path.join(__dirname, "settings.json"));
return this.defaults;
}
};
};
+9
View File
@@ -0,0 +1,9 @@
svg:not(:root) {
overflow: hidden;
}
#foo {
&:before {
content: icon(audio);
}
}
+289
View File
@@ -0,0 +1,289 @@
.ambientlight,
.no-applicationcache,
.audio,
.no-batteryapi,
.blobconstructor,
.no-canvas,
.canvastext,
.no-contenteditable,
.contextmenu,
.no-cookies,
.cors,
.no-cryptography,
.customelements,
.no-customprotocolhandler,
.customevent,
.no-dart,
.dataview,
.no-emoji,
.eventlistener,
.no-exiforientation,
.flash,
.no-forcetouch,
.fullscreen,
.no-gamepads,
.geolocation,
.no-hashchange,
.hiddenscroll,
.no-history,
.htmlimports,
.no-ie8compat,
.indexeddb,
.no-indexeddbblob,
.input,
.no-inputsearchevent,
.inputtypes,
.no-intl,
.json,
.no-ligatures,
.olreversed,
.no-mathml,
.mediasource,
.no-messagechannel,
.notification,
.no-pagevisibility,
.performance,
.no-pointerevents,
.pointerlock,
.no-postmessage,
.proximity,
.no-queryselector,
.quotamanagement,
.no-requestanimationframe,
.serviceworker,
.no-svg,
.templatestrings,
.no-touchevents,
.typedarrays,
.no-unicoderange,
.unicode,
.no-userdata,
.vibrate,
.no-video,
.vml,
.no-webintents,
.webanimations,
.no-webgl,
.websockets,
.no-xdomainrequest,
.adownload,
.no-audioautoplay,
.audioloop,
.no-audiopreload,
.webaudio,
.no-lowbattery,
.canvasblending,
.no-todataurljpeg, .todataurlpng, .todataurlwebp,
.canvaswinding,
.no-getrandomvalues,
.cssall,
.no-cssanimations,
.appearance,
.no-backdropfilter,
.backgroundblendmode,
.no-backgroundcliptext,
.bgpositionshorthand,
.no-bgpositionxy,
.bgrepeatspace, .bgrepeatround,
.no-backgroundsize,
.bgsizecover,
.no-borderimage,
.borderradius,
.no-boxdecorationbreak,
.boxshadow,
.no-boxsizing,
.csscalc,
.no-checked,
.csschunit,
.no-csscolumns,
.cssgrid, .cssgridlegacy,
.no-cubicbezierrange,
.customproperties,
.no-display-runin,
.displaytable,
.no-ellipsis,
.cssescape,
.no-cssexunit,
.cssfilters,
.no-flexbox,
.flexboxlegacy,
.no-flexboxtweener,
.flexwrap,
.no-focuswithin,
.fontdisplay,
.no-fontface,
.generatedcontent,
.no-cssgradients,
.hairline,
.no-hsla,
.csshyphens, .softhyphens, .softhyphensfind,
.no-cssinvalid,
.lastchild,
.no-cssmask,
.mediaqueries,
.no-multiplebgs,
.nthchild,
.no-objectfit,
.opacity,
.no-overflowscrolling,
.csspointerevents,
.no-csspositionsticky,
.csspseudoanimations,
.no-csspseudotransitions,
.cssreflections,
.no-regions,
.cssremunit,
.no-cssresize,
.rgba,
.no-cssscrollbar,
.scrollsnappoints,
.no-shapes,
.siblinggeneral,
.no-subpixelfont,
.supports,
.no-target,
.textalignlast,
.no-textdecoration,
.textshadow,
.no-csstransforms,
.csstransforms3d,
.no-csstransformslevel2,
.preserve3d,
.no-csstransitions,
.userselect,
.no-cssvalid,
.variablefonts,
.no-cssvhunit,
.cssvmaxunit,
.no-cssvminunit,
.cssvwunit,
.no-willchange,
.wrapflow,
.no-classlist,
.createelementattrs, .createelement-attrs,
.no-dataset,
.documentfragment,
.no-hidden,
.intersectionobserver,
.no-microdata,
.mutationobserver,
.no-passiveeventlisteners,
.bdi,
.no-datalistelem,
.details,
.no-outputelem,
.picture,
.no-progressbar, .meter,
.ruby,
.no-template,
.time,
.no-texttrackapi, .track,
.unknownelements,
.no-es5array,
.es5date,
.no-es5function,
.es5object,
.no-es5,
.strictmode,
.no-es5string,
.es5syntax,
.no-es5undefined,
.es6array,
.no-arrow,
.es6collections,
.no-contains,
.generators,
.no-es6math,
.es6number,
.no-es6object,
.promises,
.no-es6string,
.devicemotion, .deviceorientation,
.no-oninput,
.filereader,
.no-filesystem,
.capture,
.no-fileinput,
.directory,
.no-formattribute,
.localizednumber,
.no-placeholder,
.requestautocomplete,
.no-formvalidation,
.sandbox,
.no-seamless,
.srcdoc,
.no-apng,
.imgcrossorigin,
.no-jpeg2000,
.jpegxr,
.no-sizes,
.srcset,
.no-webpalpha,
.webpanimation,
.no-webplossless, .webp-lossless,
.webp,
.no-inputformaction,
.inputformenctype,
.no-inputformmethod,
.inputformnovalidate,
.no-inputformtarget,
.hovermq,
.no-pointermq,
.beacon,
.no-connectioneffectivetype,
.lowbandwidth,
.no-eventsource,
.fetch,
.no-xhrresponsetypearraybuffer,
.xhrresponsetypeblob,
.no-xhrresponsetypedocument,
.xhrresponsetypejson,
.no-xhrresponsetypetext,
.xhrresponsetype,
.no-xhr2,
.scriptasync,
.no-scriptdefer,
.speechrecognition,
.no-speechsynthesis,
.localstorage,
.no-sessionstorage,
.websqldatabase,
.no-stylescoped,
.svgasimg,
.no-svgclippaths,
.svgfilters,
.no-svgforeignobject,
.inlinesvg,
.no-smil,
.textareamaxlength,
.no-bloburls,
.datauri,
.no-urlparser,
.urlsearchparams,
.no-videoautoplay,
.videocrossorigin,
.no-videoloop,
.videopreload,
.no-publicKeyCredential,
.webglextensions,
.no-datachannel,
.getusermedia,
.no-peerconnection,
.websocketsbinary,
.no-atobbtoa,
.framed,
.no-matchmedia,
.blobworkers,
.no-dataworkers,
.sharedworkers,
.no-transferables,
.webworkers {
background: red;
}
.prefixed-cors,
.prefixed-input,
.prefixed-no-smil {
background: blue;
}
+387
View File
@@ -0,0 +1,387 @@
/* jshint sub: true */
/* global define */
define(["Modernizr"], function (M) {
var tests = {
"standalone": {
"ambientlight": M.ambientlight,
"applicationcache": M.applicationcache,
"audio": M.audio,
"battery": M.batteryapi,
"blob": M.blobconstructor,
"canvas": M.canvas,
"canvastext": M.canvastext,
"contenteditable": M.contenteditable,
"contextmenu": M.contextmenu,
"cookies": M.cookies,
"cors": M.cors,
"crypto": M.cryptography,
"custom-elements": M.customelements,
"custom-protocol-handler": M.customprotocolhandler,
"customevent": M.customevent,
"dart": M.dart,
"dataview-api": M.dataview,
"emoji": M.emoji,
"eventlistener": M.eventlistener,
"exif-orientation": M.exiforientation,
"flash": M.flash,
"forcetouch": M.forcetouch,
"fullscreen-api": M.fullscreen,
"gamepad": M.gamepads,
"geolocation": M.geolocation,
"hashchange": M.hashchange,
"hiddenscroll": M.hiddenscroll,
"history": M.history,
"htmlimports": M.htmlimports,
"ie8compat": M.ie8compat,
"indexeddb": M.indexeddb,
"indexeddbblob": M.indexeddbblob,
"input": M.input,
"inputsearchevent": M.inputsearchevent,
"inputtypes": M.inputtypes,
"intl": M.intl,
"json": M.json,
"ligatures": M.ligatures,
"lists-reversed": M.olreversed,
"mathml": M.mathml,
"media-source-extension-api": M.mediasource,
"messagechannel": M.messagechannel,
"notification": M.notification,
"pagevisibility-api": M.pagevisibility,
"performance": M.performance,
"pointerevents": M.pointerevents,
"pointerlock-api": M.pointerlock,
"postmessage": M.postmessage,
"proximity": M.proximity,
"queryselector": M.queryselector,
"quota-management-api": M.quotamanagement,
"requestanimationframe": M.requestanimationframe,
"serviceworker": M.serviceworker,
"svg": M.svg,
"templatestrings": M.templatestrings,
"touchevents": M.touchevents,
"typed-arrays": M.typedarrays,
"unicode-range": M.unicoderange,
"unicode": M.unicode,
"userdata": M.userdata,
"vibration": M.vibrate,
"video": M.video,
"vml": M.vml,
"web-intents": M.webintents,
"webanimations": M.webanimations,
"webgl": M.webgl,
"websockets": M.websockets,
"xdomainrequest": M.xdomainrequest
},
"a": {
"download": M.adownload
},
"audio": {
"autoplay": M.audioautoplay,
"loop": M.audioloop,
"preload": M.audiopreload,
"webaudio": M.webaudio
},
"battery": {
"lowbattery": M.lowbattery
},
"canvas": {
"blending": M.canvasblending,
"todataurl": [
M["todataurljpeg"],
M["todataurlpng"],
M["todataurlwebp"]
],
"winding": M.canvaswinding
},
"crypto": {
"getrandomvalues": M.getrandomvalues
},
"css": {
"all": M.cssall,
"animations": M.cssanimations,
"appearance": M.appearance,
"backdropfilter": M.backdropfilter,
"backgroundblendmode": M.backgroundblendmode,
"backgroundcliptext": M.backgroundcliptext,
"backgroundposition-shorthand": M.bgpositionshorthand,
"backgroundposition-xy": M.bgpositionxy,
"backgroundrepeat": [
M["bgrepeatspace"],
M["bgrepeatround"]
],
"backgroundsize": M.backgroundsize,
"backgroundsizecover": M.bgsizecover,
"borderimage": M.borderimage,
"borderradius": M.borderradius,
"boxdecorationbreak": M.boxdecorationbreak,
"boxshadow": M.boxshadow,
"boxsizing": M.boxsizing,
"calc": M.csscalc,
"checked": M.checked,
"chunit": M.csschunit,
"columns": M.csscolumns,
"cssgrid": [
M["cssgrid"],
M["cssgridlegacy"]
],
"cubicbezierrange": M.cubicbezierrange,
"customproperties": M.customproperties,
"displayrunin": M["display-runin"],
"displaytable": M.displaytable,
"ellipsis": M.ellipsis,
"escape": M.cssescape,
"exunit": M.cssexunit,
"filters": M.cssfilters,
"flexbox": M.flexbox,
"flexboxlegacy": M.flexboxlegacy,
"flexboxtweener": M.flexboxtweener,
"flexwrap": M.flexwrap,
"focuswithin": M.focuswithin,
"fontdisplay": M.fontdisplay,
"fontface": M.fontface,
"generatedcontent": M.generatedcontent,
"gradients": M.cssgradients,
"hairline": M.hairline,
"hsla": M.hsla,
"hyphens": [
M["csshyphens"],
M["softhyphens"],
M["softhyphensfind"]
],
"invalid": M.cssinvalid,
"lastchild": M.lastchild,
"mask": M.cssmask,
"mediaqueries": M.mediaqueries,
"multiplebgs": M.multiplebgs,
"nthchild": M.nthchild,
"objectfit": M.objectfit,
"opacity": M.opacity,
"overflow-scrolling": M.overflowscrolling,
"pointerevents": M.csspointerevents,
"positionsticky": M.csspositionsticky,
"pseudoanimations": M.csspseudoanimations,
"pseudotransitions": M.csspseudotransitions,
"reflections": M.cssreflections,
"regions": M.regions,
"remunit": M.cssremunit,
"resize": M.cssresize,
"rgba": M.rgba,
"scrollbars": M.cssscrollbar,
"scrollsnappoints": M.scrollsnappoints,
"shapes": M.shapes,
"siblinggeneral": M.siblinggeneral,
"subpixelfont": M.subpixelfont,
"supports": M.supports,
"target": M.target,
"textalignlast": M.textalignlast,
"textdecoration": M.textdecoration,
"textshadow": M.textshadow,
"transforms": M.csstransforms,
"transforms3d": M.csstransforms3d,
"transformslevel2": M.csstransformslevel2,
"transformstylepreserve3d": M.preserve3d,
"transitions": M.csstransitions,
"userselect": M.userselect,
"valid": M.cssvalid,
"variablefonts": M.variablefonts,
"vhunit": M.cssvhunit,
"vmaxunit": M.cssvmaxunit,
"vminunit": M.cssvminunit,
"vwunit": M.cssvwunit,
"will-change": M.willchange,
"wrapflow": M.wrapflow
},
"dom": {
"classlist": M.classlist,
"createElement-attrs": [
M["createelementattrs"],
M["createelement-attrs"]
],
"dataset": M.dataset,
"documentfragment": M.documentfragment,
"hidden": M.hidden,
"intersection-observer": M.intersectionobserver,
"microdata": M.microdata,
"mutationObserver": M.mutationobserver,
"passiveeventlisteners": M.passiveeventlisteners
},
"elem": {
"bdi": M.bdi,
"datalist": M.datalistelem,
"details": M.details,
"output": M.outputelem,
"picture": M.picture,
"progress-meter": [
M["progressbar"],
M["meter"]
],
"ruby": M.ruby,
"template": M.template,
"time": M.time,
"track": [
M["texttrackapi"],
M["track"]
],
"unknown": M.unknownelements
},
"es5": {
"array": M.es5array,
"date": M.es5date,
"function": M.es5function,
"object": M.es5object,
"specification": M.es5,
"strictmode": M.strictmode,
"string": M.es5string,
"syntax": M.es5syntax,
"undefined": M.es5undefined
},
"es6": {
"array": M.es6array,
"arrow": M.arrow,
"collections": M.es6collections,
"contains": M.contains,
"generators": M.generators,
"math": M.es6math,
"number": M.es6number,
"object": M.es6object,
"promises": M.promises,
"string": M.es6string
},
"event": {
"deviceorientation-motion": [
M["devicemotion"],
M["deviceorientation"]
],
"oninput": M.oninput
},
"file": {
"api": M.filereader,
"filesystem": M.filesystem
},
"forms": {
"capture": M.capture,
"fileinput": M.fileinput,
"fileinputdirectory": M.directory,
"formattribute": M.formattribute,
"inputnumber-l10n": M.localizednumber,
"placeholder": M.placeholder,
"requestautocomplete": M.requestautocomplete,
"validation": M.formvalidation
},
"iframe": {
"sandbox": M.sandbox,
"seamless": M.seamless,
"srcdoc": M.srcdoc
},
"img": {
"apng": M.apng,
"crossorigin": M.imgcrossorigin,
"jpeg2000": M.jpeg2000,
"jpegxr": M.jpegxr,
"sizes": M.sizes,
"srcset": M.srcset,
"webp-alpha": M.webpalpha,
"webp-animation": M.webpanimation,
"webp-lossless": [
M["webplossless"],
M["webp-lossless"]
],
"webp": M.webp
},
"input": {
"formaction": M.inputformaction,
"formenctype": M.inputformenctype,
"formmethod": M.inputformmethod,
"formnovalidate": M.inputformnovalidate,
"formtarget": M.inputformtarget
},
"mediaquery": {
"hovermq": M.hovermq,
"pointermq": M.pointermq
},
"network": {
"beacon": M.beacon,
"connection-effectivetype": M.connectioneffectivetype,
"connection": M.lowbandwidth,
"eventsource": M.eventsource,
"fetch": M.fetch,
"xhr-responsetype-arraybuffer": M.xhrresponsetypearraybuffer,
"xhr-responsetype-blob": M.xhrresponsetypeblob,
"xhr-responsetype-document": M.xhrresponsetypedocument,
"xhr-responsetype-json": M.xhrresponsetypejson,
"xhr-responsetype-text": M.xhrresponsetypetext,
"xhr-responsetype": M.xhrresponsetype,
"xhr2": M.xhr2
},
"script": {
"async": M.scriptasync,
"defer": M.scriptdefer
},
"speech": {
"speech-recognition": M.speechrecognition,
"speech-synthesis": M.speechsynthesis
},
"storage": {
"localstorage": M.localstorage,
"sessionstorage": M.sessionstorage,
"websqldatabase": M.websqldatabase
},
"style": {
"scoped": M.stylescoped
},
"svg": {
"asimg": M.svgasimg,
"clippaths": M.svgclippaths,
"filters": M.svgfilters,
"foreignobject": M.svgforeignobject,
"inline": M.inlinesvg,
"smil": M.smil
},
"textarea": {
"maxlength": M.textareamaxlength
},
"url": {
"bloburls": M.bloburls,
"data-uri": M.datauri,
"parser": M.urlparser,
"urlsearchparams": M.urlsearchparams
},
"video": {
"autoplay": M.videoautoplay,
"crossorigin": M.videocrossorigin,
"loop": M.videoloop,
"preload": M.videopreload
},
"webauthn": {
"publickeycredential": M.publicKeyCredential
},
"webgl": {
"extensions": M.webglextensions
},
"webrtc": {
"datachannel": M.datachannel,
"getusermedia": M.getusermedia,
"peerconnection": M.peerconnection
},
"websockets": {
"binary": M.websocketsbinary
},
"window": {
"atob-btoa": M.atobbtoa,
"framed": M.framed,
"matchmedia": M.matchmedia
},
"workers": {
"blobworkers": M.blobworkers,
"dataworkers": M.dataworkers,
"sharedworkers": M.sharedworkers,
"transferables": M.transferables,
"webworkers": M.webworkers
}
};
return M;
});
+382
View File
@@ -0,0 +1,382 @@
/* jshint sub: true */
var Modernizr = window.Modernizr;
var tests = {
"standalone": {
"ambientlight": Modernizr.ambientlight,
"applicationcache": Modernizr.applicationcache,
"audio": Modernizr.audio,
"battery": Modernizr.batteryapi,
"blob": Modernizr.blobconstructor,
"canvas": Modernizr.canvas,
"canvastext": Modernizr.canvastext,
"contenteditable": Modernizr.contenteditable,
"contextmenu": Modernizr.contextmenu,
"cookies": Modernizr.cookies,
"cors": Modernizr.cors,
"crypto": Modernizr.cryptography,
"custom-elements": Modernizr.customelements,
"custom-protocol-handler": Modernizr.customprotocolhandler,
"customevent": Modernizr.customevent,
"dart": Modernizr.dart,
"dataview-api": Modernizr.dataview,
"emoji": Modernizr.emoji,
"eventlistener": Modernizr.eventlistener,
"exif-orientation": Modernizr.exiforientation,
"flash": Modernizr.flash,
"forcetouch": Modernizr.forcetouch,
"fullscreen-api": Modernizr.fullscreen,
"gamepad": Modernizr.gamepads,
"geolocation": Modernizr.geolocation,
"hashchange": Modernizr.hashchange,
"hiddenscroll": Modernizr.hiddenscroll,
"history": Modernizr.history,
"htmlimports": Modernizr.htmlimports,
"ie8compat": Modernizr.ie8compat,
"indexeddb": Modernizr.indexeddb,
"indexeddbblob": Modernizr.indexeddbblob,
"input": Modernizr.input,
"inputsearchevent": Modernizr.inputsearchevent,
"inputtypes": Modernizr.inputtypes,
"intl": Modernizr.intl,
"json": Modernizr.json,
"ligatures": Modernizr.ligatures,
"lists-reversed": Modernizr.olreversed,
"mathml": Modernizr.mathml,
"media-source-extension-api": Modernizr.mediasource,
"messagechannel": Modernizr.messagechannel,
"notification": Modernizr.notification,
"pagevisibility-api": Modernizr.pagevisibility,
"performance": Modernizr.performance,
"pointerevents": Modernizr.pointerevents,
"pointerlock-api": Modernizr.pointerlock,
"postmessage": Modernizr.postmessage,
"proximity": Modernizr.proximity,
"queryselector": Modernizr.queryselector,
"quota-management-api": Modernizr.quotamanagement,
"requestanimationframe": Modernizr.requestanimationframe,
"serviceworker": Modernizr.serviceworker,
"svg": Modernizr.svg,
"templatestrings": Modernizr.templatestrings,
"touchevents": Modernizr.touchevents,
"typed-arrays": Modernizr.typedarrays,
"unicode-range": Modernizr.unicoderange,
"unicode": Modernizr.unicode,
"userdata": Modernizr.userdata,
"vibration": Modernizr.vibrate,
"video": Modernizr.video,
"vml": Modernizr.vml,
"web-intents": Modernizr.webintents,
"webanimations": Modernizr.webanimations,
"webgl": Modernizr.webgl,
"websockets": Modernizr.websockets,
"xdomainrequest": Modernizr.xdomainrequest
},
"a": {
"download": Modernizr.adownload
},
"audio": {
"autoplay": Modernizr.audioautoplay,
"loop": Modernizr.audioloop,
"preload": Modernizr.audiopreload,
"webaudio": Modernizr.webaudio
},
"battery": {
"lowbattery": Modernizr.lowbattery
},
"canvas": {
"blending": Modernizr.canvasblending,
"todataurl": [
Modernizr["todataurljpeg"],
Modernizr["todataurlpng"],
Modernizr["todataurlwebp"]
],
"winding": Modernizr.canvaswinding
},
"crypto": {
"getrandomvalues": Modernizr.getrandomvalues
},
"css": {
"all": Modernizr.cssall,
"animations": Modernizr.cssanimations,
"appearance": Modernizr.appearance,
"backdropfilter": Modernizr.backdropfilter,
"backgroundblendmode": Modernizr.backgroundblendmode,
"backgroundcliptext": Modernizr.backgroundcliptext,
"backgroundposition-shorthand": Modernizr.bgpositionshorthand,
"backgroundposition-xy": Modernizr.bgpositionxy,
"backgroundrepeat": [
Modernizr["bgrepeatspace"],
Modernizr["bgrepeatround"]
],
"backgroundsize": Modernizr.backgroundsize,
"backgroundsizecover": Modernizr.bgsizecover,
"borderimage": Modernizr.borderimage,
"borderradius": Modernizr.borderradius,
"boxdecorationbreak": Modernizr.boxdecorationbreak,
"boxshadow": Modernizr.boxshadow,
"boxsizing": Modernizr.boxsizing,
"calc": Modernizr.csscalc,
"checked": Modernizr.checked,
"chunit": Modernizr.csschunit,
"columns": Modernizr.csscolumns,
"cssgrid": [
Modernizr["cssgrid"],
Modernizr["cssgridlegacy"]
],
"cubicbezierrange": Modernizr.cubicbezierrange,
"customproperties": Modernizr.customproperties,
"displayrunin": Modernizr["display-runin"],
"displaytable": Modernizr.displaytable,
"ellipsis": Modernizr.ellipsis,
"escape": Modernizr.cssescape,
"exunit": Modernizr.cssexunit,
"filters": Modernizr.cssfilters,
"flexbox": Modernizr.flexbox,
"flexboxlegacy": Modernizr.flexboxlegacy,
"flexboxtweener": Modernizr.flexboxtweener,
"flexwrap": Modernizr.flexwrap,
"focuswithin": Modernizr.focuswithin,
"fontdisplay": Modernizr.fontdisplay,
"fontface": Modernizr.fontface,
"generatedcontent": Modernizr.generatedcontent,
"gradients": Modernizr.cssgradients,
"hairline": Modernizr.hairline,
"hsla": Modernizr.hsla,
"hyphens": [
Modernizr["csshyphens"],
Modernizr["softhyphens"],
Modernizr["softhyphensfind"]
],
"invalid": Modernizr.cssinvalid,
"lastchild": Modernizr.lastchild,
"mask": Modernizr.cssmask,
"mediaqueries": Modernizr.mediaqueries,
"multiplebgs": Modernizr.multiplebgs,
"nthchild": Modernizr.nthchild,
"objectfit": Modernizr.objectfit,
"opacity": Modernizr.opacity,
"overflow-scrolling": Modernizr.overflowscrolling,
"pointerevents": Modernizr.csspointerevents,
"positionsticky": Modernizr.csspositionsticky,
"pseudoanimations": Modernizr.csspseudoanimations,
"pseudotransitions": Modernizr.csspseudotransitions,
"reflections": Modernizr.cssreflections,
"regions": Modernizr.regions,
"remunit": Modernizr.cssremunit,
"resize": Modernizr.cssresize,
"rgba": Modernizr.rgba,
"scrollbars": Modernizr.cssscrollbar,
"scrollsnappoints": Modernizr.scrollsnappoints,
"shapes": Modernizr.shapes,
"siblinggeneral": Modernizr.siblinggeneral,
"subpixelfont": Modernizr.subpixelfont,
"supports": Modernizr.supports,
"target": Modernizr.target,
"textalignlast": Modernizr.textalignlast,
"textdecoration": Modernizr.textdecoration,
"textshadow": Modernizr.textshadow,
"transforms": Modernizr.csstransforms,
"transforms3d": Modernizr.csstransforms3d,
"transformslevel2": Modernizr.csstransformslevel2,
"transformstylepreserve3d": Modernizr.preserve3d,
"transitions": Modernizr.csstransitions,
"userselect": Modernizr.userselect,
"valid": Modernizr.cssvalid,
"variablefonts": Modernizr.variablefonts,
"vhunit": Modernizr.cssvhunit,
"vmaxunit": Modernizr.cssvmaxunit,
"vminunit": Modernizr.cssvminunit,
"vwunit": Modernizr.cssvwunit,
"will-change": Modernizr.willchange,
"wrapflow": Modernizr.wrapflow
},
"dom": {
"classlist": Modernizr.classlist,
"createElement-attrs": [
Modernizr["createelementattrs"],
Modernizr["createelement-attrs"]
],
"dataset": Modernizr.dataset,
"documentfragment": Modernizr.documentfragment,
"hidden": Modernizr.hidden,
"intersection-observer": Modernizr.intersectionobserver,
"microdata": Modernizr.microdata,
"mutationObserver": Modernizr.mutationobserver,
"passiveeventlisteners": Modernizr.passiveeventlisteners
},
"elem": {
"bdi": Modernizr.bdi,
"datalist": Modernizr.datalistelem,
"details": Modernizr.details,
"output": Modernizr.outputelem,
"picture": Modernizr.picture,
"progress-meter": [
Modernizr["progressbar"],
Modernizr["meter"]
],
"ruby": Modernizr.ruby,
"template": Modernizr.template,
"time": Modernizr.time,
"track": [
Modernizr["texttrackapi"],
Modernizr["track"]
],
"unknown": Modernizr.unknownelements
},
"es5": {
"array": Modernizr.es5array,
"date": Modernizr.es5date,
"function": Modernizr.es5function,
"object": Modernizr.es5object,
"specification": Modernizr.es5,
"strictmode": Modernizr.strictmode,
"string": Modernizr.es5string,
"syntax": Modernizr.es5syntax,
"undefined": Modernizr.es5undefined
},
"es6": {
"array": Modernizr.es6array,
"arrow": Modernizr.arrow,
"collections": Modernizr.es6collections,
"contains": Modernizr.contains,
"generators": Modernizr.generators,
"math": Modernizr.es6math,
"number": Modernizr.es6number,
"object": Modernizr.es6object,
"promises": Modernizr.promises,
"string": Modernizr.es6string
},
"event": {
"deviceorientation-motion": [
Modernizr["devicemotion"],
Modernizr["deviceorientation"]
],
"oninput": Modernizr.oninput
},
"file": {
"api": Modernizr.filereader,
"filesystem": Modernizr.filesystem
},
"forms": {
"capture": Modernizr.capture,
"fileinput": Modernizr.fileinput,
"fileinputdirectory": Modernizr.directory,
"formattribute": Modernizr.formattribute,
"inputnumber-l10n": Modernizr.localizednumber,
"placeholder": Modernizr.placeholder,
"requestautocomplete": Modernizr.requestautocomplete,
"validation": Modernizr.formvalidation
},
"iframe": {
"sandbox": Modernizr.sandbox,
"seamless": Modernizr.seamless,
"srcdoc": Modernizr.srcdoc
},
"img": {
"apng": Modernizr.apng,
"crossorigin": Modernizr.imgcrossorigin,
"jpeg2000": Modernizr.jpeg2000,
"jpegxr": Modernizr.jpegxr,
"sizes": Modernizr.sizes,
"srcset": Modernizr.srcset,
"webp-alpha": Modernizr.webpalpha,
"webp-animation": Modernizr.webpanimation,
"webp-lossless": [
Modernizr["webplossless"],
Modernizr["webp-lossless"]
],
"webp": Modernizr.webp
},
"input": {
"formaction": Modernizr.inputformaction,
"formenctype": Modernizr.inputformenctype,
"formmethod": Modernizr.inputformmethod,
"formnovalidate": Modernizr.inputformnovalidate,
"formtarget": Modernizr.inputformtarget
},
"mediaquery": {
"hovermq": Modernizr.hovermq,
"pointermq": Modernizr.pointermq
},
"network": {
"beacon": Modernizr.beacon,
"connection-effectivetype": Modernizr.connectioneffectivetype,
"connection": Modernizr.lowbandwidth,
"eventsource": Modernizr.eventsource,
"fetch": Modernizr.fetch,
"xhr-responsetype-arraybuffer": Modernizr.xhrresponsetypearraybuffer,
"xhr-responsetype-blob": Modernizr.xhrresponsetypeblob,
"xhr-responsetype-document": Modernizr.xhrresponsetypedocument,
"xhr-responsetype-json": Modernizr.xhrresponsetypejson,
"xhr-responsetype-text": Modernizr.xhrresponsetypetext,
"xhr-responsetype": Modernizr.xhrresponsetype,
"xhr2": Modernizr.xhr2
},
"script": {
"async": Modernizr.scriptasync,
"defer": Modernizr.scriptdefer
},
"speech": {
"speech-recognition": Modernizr.speechrecognition,
"speech-synthesis": Modernizr.speechsynthesis
},
"storage": {
"localstorage": Modernizr.localstorage,
"sessionstorage": Modernizr.sessionstorage,
"websqldatabase": Modernizr.websqldatabase
},
"style": {
"scoped": Modernizr.stylescoped
},
"svg": {
"asimg": Modernizr.svgasimg,
"clippaths": Modernizr.svgclippaths,
"filters": Modernizr.svgfilters,
"foreignobject": Modernizr.svgforeignobject,
"inline": Modernizr.inlinesvg,
"smil": Modernizr.smil
},
"textarea": {
"maxlength": Modernizr.textareamaxlength
},
"url": {
"bloburls": Modernizr.bloburls,
"data-uri": Modernizr.datauri,
"parser": Modernizr.urlparser,
"urlsearchparams": Modernizr.urlsearchparams
},
"video": {
"autoplay": Modernizr.videoautoplay,
"crossorigin": Modernizr.videocrossorigin,
"loop": Modernizr.videoloop,
"preload": Modernizr.videopreload
},
"webauthn": {
"publickeycredential": Modernizr.publicKeyCredential
},
"webgl": {
"extensions": Modernizr.webglextensions
},
"webrtc": {
"datachannel": Modernizr.datachannel,
"getusermedia": Modernizr.getusermedia,
"peerconnection": Modernizr.peerconnection
},
"websockets": {
"binary": Modernizr.websocketsbinary
},
"window": {
"atob-btoa": Modernizr.atobbtoa,
"framed": Modernizr.framed,
"matchmedia": Modernizr.matchmedia
},
"workers": {
"blobworkers": Modernizr.blobworkers,
"dataworkers": Modernizr.dataworkers,
"sharedworkers": Modernizr.sharedworkers,
"transferables": Modernizr.transferables,
"webworkers": Modernizr.webworkers
}
};
+134
View File
@@ -0,0 +1,134 @@
"use strict";
var fs = require("fs-extra");
var cwd = process.cwd();
var path = require("path");
var buildDir = path.join(cwd, "build");
if (fs.existsSync(buildDir)) {
console.log("Removing old build folder...");
fs.removeSync(buildDir);
console.log("Done.");
}
var modernizr = require("modernizr");
modernizr.metadata(function (metadata) {
var props = metadata.map(function (data) {
return {
name: data.name,
path: data.amdPath.replace("test/", ""),
property: data.property,
cssclass: data.cssclass
};
});
var jsProps = props.filter(function (data) {
return data.property;
});
var jsTests = {};
jsProps.forEach(function (prop) {
var sections = prop.path.split("/");
var maxLength = sections.length - 1;
var current = jsTests;
if (sections.length === 1) {
current.standalone = current.standalone || {};
current.standalone[sections[0]] = prop.property;
} else {
sections.forEach(function (sect, idx) {
if (idx === maxLength) {
current[sect] = prop.property;
} else {
current[sect] = current[sect] || {};
}
current = current[sect];
});
}
});
var string = JSON.stringify(jsTests, null, "\t");
// Convert "foo": "bar" -> "foo": Modernizr.bar
string = string.replace(/(\:(?:\s)?)\"(.*)\"/g, "$1Modernizr.$2");
// Convert [ "foo": "bar" ] -> [ "foo": Modernizr["bar"] ]
string = string.replace(/(\t{3})\"(.*)\"/g, "$1Modernizr[\"$2\"]");
// Convert Modernizr.foo-bar -> Modernizr["foo-bar"]
string = string.replace(/\.((?:[\w]+)?\-(?:[\w]+))/g, "[\"$1\"]");
// Save test/js/vanilla.js
(function () {
var vanilla = [
"/* jshint sub: true */",
"var Modernizr = window.Modernizr;",
"var tests = " + string + ";"
].join("\n\n");
fs.writeFileSync(path.join(cwd, "test", "js", "vanilla.js"), vanilla);
}());
// Save test/js/amd.js
(function () {
var amd = [
"/* jshint sub: true */\n/* global define */",
"define([\"Modernizr\"], function (M) {",
" var tests = " + string.replace(/\n/g, "\n\t").replace(/Modernizr/g, "M") + ";",
" return M;",
"});"
].join("\n\n");
fs.writeFileSync(path.join(cwd, "test", "js", "amd.js"), amd);
}());
// Save test/css/vanilla.css
(function () {
var cssProps = props.filter(function (data) {
return data.cssclass;
}).map(function (data, idx) {
var cssclass = Array.isArray(data.cssclass) ? data.cssclass : [data.cssclass];
var negateclass = (idx % 2 ? "no-" : "");
return "." + negateclass + cssclass.join(", .");
});
var css = [
cssProps.join(",\n") + " {",
"\tbackground: red;",
"}\n\n"
].join("\n");
// Take a few classes and add a prefix for testing.
var prefixed = [
".prefixed-cors",
".prefixed-input",
".prefixed-no-smil"
];
css += [
prefixed.join(",\n") + " {",
"\tbackground: blue;",
"}\n"
].join("\n");
fs.writeFileSync(path.join(cwd, "test", "css", "vanilla.css"), css);
}());
var missingData = props.filter(function (data) {
return !data.property && !data.cssclass;
}).map(function (data) {
return data.path;
});
if (missingData.length) {
console.log("\n");
console.log("The following tests have no metadata:");
console.log();
console.log(missingData.join("\n"));
}
});
+17
View File
@@ -0,0 +1,17 @@
var Mocha = require("mocha");
var mocha = new Mocha({
setup : "bdd",
reporter : process.env.TRAVIS ? "tap" : "spec",
slow : 5000,
timeout : 30000
});
mocha.addFile("test/tests.js");
var runner = mocha.run();
runner.on("fail", function (test, err) {
process.stderr.write(" " + err.toString() + "\n\n");
process.exit(1);
});
+9
View File
@@ -0,0 +1,9 @@
{
"dest": "build/modernizr-custom.js",
"cache": false,
"files": {
"src": [
"test/{js,css}/*"
]
}
}
+8
View File
@@ -0,0 +1,8 @@
{
"dest": "build/modernizr-custom.js",
"files": {
"src": [
"test/{js,css}/*"
]
}
}
+14
View File
@@ -0,0 +1,14 @@
{
"dest": "build/modernizr-exclude.js",
"excludeTests": [
"applicationcache",
"emoji",
"notification"
],
"files": {
"src": [
"test/{js,css}/*"
]
},
"uglify": false
}
+10
View File
@@ -0,0 +1,10 @@
{
"classPrefix": "prefixed-",
"dest": "build/modernizr-prefixed.js",
"uglify": false,
"files": {
"src": [
"test/css/*.css"
]
}
}
+15
View File
@@ -0,0 +1,15 @@
{
"dest": "build/modernizr-select.js",
"tests": [
"webintents",
"siblinggeneral",
"svg"
],
"files": {
"src": [
"test/{js,css}/*"
]
},
"crawl": false,
"uglify": false
}
+363
View File
@@ -0,0 +1,363 @@
/*global describe, before, it, after*/
"use strict";
var fs = require("fs");
var path = require("path");
var colors = require("colors");
var cwd = process.cwd();
var chai = require('chai');
var expect = chai.expect;
var nexpect = require("nexpect");
var tests = "adownload, ambientlight, apng, appearance, applicationcache, arrow, atobbtoa, audio, audioautoplay, audioloop, audiopreload, backdropfilter, backgroundblendmode, backgroundcliptext, backgroundsize, batteryapi, bdi, beacon, bgpositionshorthand, bgpositionxy, bgrepeatround, bgrepeatspace, bgsizecover, blobconstructor, bloburls, blobworkers, borderimage, borderradius, boxdecorationbreak, boxshadow, boxsizing, canvas, canvasblending, canvastext, canvaswinding, capture, checked, classlist, connectioneffectivetype, contains, contenteditable, contextmenu, cookies, cors, createelement-attrs, createelementattrs, cryptography, cssall, cssanimations, csscalc, csschunit, csscolumns, cssescape, cssexunit, cssfilters, cssgradients, cssgrid, cssgridlegacy, csshyphens, cssinvalid, cssmask, csspointerevents, csspositionsticky, csspseudoanimations, csspseudotransitions, cssreflections, cssremunit, cssresize, cssscrollbar, csstransforms, csstransforms3d, csstransformslevel2, csstransitions, cssvalid, cssvhunit, cssvmaxunit, cssvminunit, cssvwunit, cubicbezierrange, customelements, customevent, customproperties, customprotocolhandler, dart, datachannel, datalistelem, dataset, datauri, dataview, dataworkers, details, devicemotion, deviceorientation, directory, display-runin, displaytable, documentfragment, ellipsis, emoji, es5, es5array, es5date, es5function, es5object, es5string, es5syntax, es5undefined, es6array, es6collections, es6math, es6number, es6object, es6string, eventlistener, eventsource, exiforientation, fetch, fileinput, filereader, filesystem, flash, flexbox, flexboxlegacy, flexboxtweener, flexwrap, focuswithin, fontdisplay, fontface, forcetouch, formattribute, formvalidation, framed, fullscreen, gamepads, generatedcontent, generators, geolocation, getrandomvalues, getusermedia, hairline, hashchange, hidden, hiddenscroll, history, hovermq, hsla, htmlimports, ie8compat, imgcrossorigin, indexeddb, indexeddbblob, inlinesvg, input, inputformaction, inputformenctype, inputformmethod, inputformnovalidate, inputformtarget, inputsearchevent, inputtypes, intersectionobserver, intl, jpeg2000, jpegxr, json, lastchild, ligatures, localizednumber, localstorage, lowbandwidth, lowbattery, matchmedia, mathml, mediaqueries, mediasource, messagechannel, meter, microdata, multiplebgs, mutationobserver, notification, nthchild, objectfit, olreversed, oninput, opacity, outputelem, overflowscrolling, pagevisibility, passiveeventlisteners, peerconnection, performance, picture, placeholder, pointerevents, pointerlock, pointermq, postmessage, preserve3d, progressbar, promises, proximity, publicKeyCredential, queryselector, quotamanagement, regions, requestanimationframe, requestautocomplete, rgba, ruby, sandbox, scriptasync, scriptdefer, scrollsnappoints, seamless, serviceworker, sessionstorage, shapes, sharedworkers, siblinggeneral, sizes, smil, softhyphens, softhyphensfind, speechrecognition, speechsynthesis, srcdoc, srcset, strictmode, stylescoped, subpixelfont, supports, svg, svgasimg, svgclippaths, svgfilters, svgforeignobject, target, template, templatestrings, textalignlast, textareamaxlength, textdecoration, textshadow, texttrackapi, time, todataurljpeg, todataurlpng, todataurlwebp, touchevents, track, transferables, typedarrays, unicode, unicoderange, unknownelements, urlparser, urlsearchparams, userdata, userselect, variablefonts, vibrate, video, videoautoplay, videocrossorigin, videoloop, videopreload, vml, webanimations, webaudio, webgl, webglextensions, webintents, webp, webp-lossless, webpalpha, webpanimation, webplossless, websockets, websocketsbinary, websqldatabase, webworkers, willchange, wrapflow, xdomainrequest, xhr2, xhrresponsetype, xhrresponsetypearraybuffer, xhrresponsetypeblob, xhrresponsetypedocument, xhrresponsetypejson, xhrresponsetypetext";
var testArray = tests.split(", ");
var cli = path.join(cwd, "bin", "customizr");
var settingsPath = path.join(cwd, "test", "settings");
var settings = {
cache: path.join(settingsPath, "cache-invalidate.json"),
custom: path.join(settingsPath, "custom.json"),
exclude: path.join(settingsPath, "exclude.json"),
prefixed: path.join(settingsPath, "prefixed.json"),
select: path.join(settingsPath, "select.json")
};
describe("customizr", function () {
var testsLength = testArray.length,
existingBuild = path.join(cwd, "build", "modernizr-custom.js");
it("should find all available tests in project", function (done) {
process.stdout.write("\n\n");
nexpect.spawn(cli, [
"--config", settings.custom
], {
stripColors: true
})
.wait("Looking for Modernizr references")
.wait(">> " + testsLength + " matches in")
.expect(">> " + tests)
.expect(">> " + testsLength + " matches in")
.expect(">> " + tests)
.expect(">> " + testsLength + " matches in")
.expect(">> " + tests)
.expect(">> Ready to build using these settings:")
.wait("Building your customized Modernizr").wait("OK")
.expect(">> Success! Saved file to build/modernizr-custom.js")
.run(function (err) {
if (!err) {
done();
} else {
throw err;
}
});
});
it("should avoid re-building a cached Modernizr build", function (done) {
process.stdout.write("\n\n");
nexpect.spawn(cli, [
"--config", settings.custom
], {
stripColors: true
})
.wait("Looking for Modernizr references")
.wait(">> " + testsLength + " matches in")
.expect(">> " + tests)
.expect(">> " + testsLength + " matches in")
.expect(">> " + tests)
.expect(">> " + testsLength + " matches in")
.expect(">> " + tests)
.expect("No config or test changes detected")
.expect(">> The build step has been bypassed. Use `--force` to override.")
.expect(">> Your current file can be found in build/modernizr-custom.js")
.run(function (err) {
if (!err) {
done();
} else {
throw err;
}
});
});
it("should force re-building a cached Modernizr build", function (done) {
process.stdout.write("\n\n");
nexpect.spawn(cli, [
"--config", settings.custom,
"--force"
], {
stripColors: true
})
.wait("Looking for Modernizr references")
.wait(">> " + testsLength + " matches in")
.expect(">> " + tests)
.expect(">> " + testsLength + " matches in")
.expect(">> " + tests)
.expect(">> " + testsLength + " matches in")
.expect(">> " + tests)
.expect(">> Ready to build using these settings:")
.wait("Building your customized Modernizr").wait("OK")
.expect(">> Success! Saved file to build/modernizr-custom.js")
.run(function (err) {
if (!err) {
done();
} else {
throw err;
}
});
});
it("should invalidate a cached Modernizr build", function (done) {
process.stdout.write("\n\n");
nexpect.spawn(cli, [
"--config", settings.cache
], {
stripColors: true
})
.wait("Looking for Modernizr references")
.wait(">> " + testsLength + " matches in")
.expect(">> " + tests)
.expect(">> " + testsLength + " matches in")
.expect(">> " + tests)
.expect(">> " + testsLength + " matches in")
.expect(">> " + tests)
.expect(">> Ready to build using these settings:")
.wait("Building your customized Modernizr").wait("OK")
.expect(">> Success! Saved file to build/modernizr-custom.js")
.run(function (err) {
if (!err) {
done();
} else {
throw err;
}
});
});
describe("should include all tests", function () {
var contents;
testArray.forEach(function (test) {
it(test, function (done) {
contents = contents || fs.readFileSync(existingBuild, "utf8");
expect(contents.indexOf(test)).to.not.equal(-1);
done();
});
});
});
});
describe("custom builds", function () {
describe("declared tests", function () {
it("should only build declared tests", function (done) {
process.stdout.write("\n\n");
nexpect.spawn(cli, [
"--config", settings.select
], {
stripColors: true
})
.wait(">> Explicitly including these tests:")
.expect(">> siblinggeneral, svg, webintents")
.expect("Skipping file traversal")
.wait("Building your customized Modernizr").wait("OK")
.expect(">> Success! Saved file to build/modernizr-select.js")
.run(function (err) {
if (!err) {
done();
} else {
throw err;
}
});
});
describe("should only contain references to bundled tests", function () {
var includedTestArray = [
"webintents",
"siblinggeneral",
"svg"
];
var testsLength = includedTestArray.length;
var includedTests = includedTestArray.join(" ");
var contents;
includedTestArray.forEach(function (test) {
it(test, function (done) {
contents = contents || fs.readFileSync(path.join(cwd, "build", "modernizr-select.js"), "utf8");
expect(contents.indexOf(test)).to.not.equal(-1);
done();
});
});
describe("should not contain these references", function (done) {
var excludedTestArray = testArray.filter(function (test) {
return includedTestArray.indexOf(test) === -1;
});
excludedTestArray.forEach(function (test) {
it(test, function (done) {
contents = contents || fs.readFileSync(path.join(cwd, "build", "modernizr-select.js"), "utf8");
var testPattern = "addTest('" + test.toLowerCase();
expect(contents.indexOf(testPattern)).to.equal(-1);
done();
});
});
});
});
});
describe("excluded tests", function () {
it("should build without excluded tests", function (done) {
process.stdout.write("\n\n");
nexpect.spawn(cli, [
"--config", settings.exclude
], {
stripColors: true
})
.wait(">> Explicitly excluding these tests:")
.expect(">> applicationcache, emoji, notification")
.wait("Building your customized Modernizr").wait("OK")
.expect(">> Success! Saved file to build/modernizr-exclude.js")
.run(function (err) {
if (!err) {
done();
} else {
throw err;
}
});
});
describe("should not contain references to excluded tests", function () {
var excludedTestArray = [
"applicationcache",
"emoji",
"notification"
];
var testsLength = excludedTestArray.length;
var excludedTests = excludedTestArray.join(" ");
var contents;
excludedTestArray.forEach(function (test) {
it(test, function (done) {
contents = contents || fs.readFileSync(path.join(cwd, "build", "modernizr-exclude.js"), "utf8");
expect(contents.indexOf(test)).to.equal(-1);
done();
});
});
});
});
describe("prefix test", function () {
it("should honor the specified prefix", function (done) {
process.stdout.write("\n\n");
nexpect.spawn(cli, [
"--config", settings.prefixed
], {
stripColors: true
})
.wait("Looking for Modernizr references")
.expect(">> 3 matches in test/css/vanilla.css")
.expect(">> cors, input, smil")
.wait("Building your customized Modernizr").wait("OK")
.expect(">> Success! Saved file to build/modernizr-prefixed.js")
.run(function (err) {
if (!err) {
done();
} else {
throw err;
}
});
});
describe("should only contain references to bundled tests", function () {
var includedTestArray = [
"cors",
"input",
"smil"
];
var testsLength = includedTestArray.length;
var includedTests = includedTestArray.join(" ");
var contents;
includedTestArray.forEach(function (test) {
it(test, function (done) {
contents = contents || fs.readFileSync(path.join(cwd, "build", "modernizr-prefixed.js"), "utf8");
expect(contents.indexOf(test)).to.not.equal(-1);
done();
});
});
describe("should not contain these references", function (done) {
var excludedTestArray = testArray.filter(function (test) {
return includedTestArray.indexOf(test) === -1;
});
excludedTestArray.forEach(function (test) {
it(test, function (done) {
contents = contents || fs.readFileSync(path.join(cwd, "build", "modernizr-prefixed.js"), "utf8");
var testPattern = "addTest('" + test.toLowerCase();
expect(contents.indexOf(testPattern)).to.equal(-1);
done();
});
});
});
});
});
});