├── .node-version ├── packages ├── sporks │ ├── .npmignore │ ├── package.json │ ├── src │ │ └── index.js │ └── __tests__ │ │ └── index.js ├── sporks-loader │ ├── .npmignore │ ├── src │ │ ├── internal-loader.js │ │ ├── preloader.js │ │ └── index.js │ └── package.json ├── webpack-graphql │ ├── .npmignore │ ├── package.json │ └── src │ │ ├── WebpackGraphQLPlugin.js │ │ ├── CompilerStatePlugin.js │ │ ├── schema.graphql │ │ └── data.js ├── webpack-dev-server-plugin │ ├── .npmignore │ ├── package.json │ └── src │ │ └── WebpackDevServerPlugin.js ├── webpack-rule-composer │ ├── .npmignore │ ├── package.json │ ├── __tests__ │ │ ├── __snapshots__ │ │ │ └── index.js.snap │ │ └── index.js │ └── src │ │ └── index.js └── webpack-enhanced-loader-plugin │ ├── .npmignore │ ├── src │ ├── webpack-compat │ │ ├── normal-module.js │ │ └── context-dependency.js │ └── EnhancedLoaderPlugin.js │ └── package.json ├── .eslintignore ├── .prettierignore ├── .gitignore ├── jest.config.js ├── babel.config.js ├── lerna.json ├── .eslintrc.js ├── prettier.config.js ├── .flowconfig ├── .github └── workflows │ └── main.yml ├── package.json ├── LICENSE └── flow-typed └── npm └── jest_v23.x.x.js /.node-version: -------------------------------------------------------------------------------- 1 | 12.10.0 2 | -------------------------------------------------------------------------------- /packages/sporks/.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | -------------------------------------------------------------------------------- /packages/sporks-loader/.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | -------------------------------------------------------------------------------- /packages/webpack-graphql/.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | -------------------------------------------------------------------------------- /packages/webpack-dev-server-plugin/.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | -------------------------------------------------------------------------------- /packages/webpack-rule-composer/.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | -------------------------------------------------------------------------------- /packages/webpack-enhanced-loader-plugin/.npmignore: -------------------------------------------------------------------------------- 1 | src 2 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | /packages/*/lib 2 | flow-typed 3 | /coverage 4 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /packages/*/lib 2 | flow-typed 3 | /coverage 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | /node_modules 3 | /packages/*/node_modules 4 | /packages/*/lib 5 | /coverage 6 | -------------------------------------------------------------------------------- /packages/webpack-enhanced-loader-plugin/src/webpack-compat/normal-module.js: -------------------------------------------------------------------------------- 1 | export function buildInfo(mod) { 2 | return mod.buildInfo; 3 | } 4 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | projects: ['/packages/*'], 3 | coverageDirectory: './coverage/', 4 | collectCoverage: true, 5 | }; 6 | -------------------------------------------------------------------------------- /packages/sporks-loader/src/internal-loader.js: -------------------------------------------------------------------------------- 1 | module.exports = function(...args) { 2 | this.cacheable(); 3 | this._module.useSourceMap = true; 4 | this.callback(null, ...args); 5 | }; 6 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: [ 3 | '@babel/transform-flow-strip-types', 4 | '@babel/transform-modules-commonjs', 5 | '@babel/syntax-object-rest-spread', 6 | ], 7 | }; 8 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "10.1.0", 3 | "useWorkspaces": true, 4 | "packages": [ 5 | "packages/*" 6 | ], 7 | "command": { 8 | "publish": { 9 | "exact": true 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: 'babel-eslint', 3 | extends: 'eslint:recommended', 4 | env: { node: true, es6: true }, 5 | overrides: { 6 | files: ['**/__tests__/**/*.js'], 7 | env: { jest: true }, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | bracketSpacing: true, 3 | jsxBracketSameLine: false, 4 | printWidth: 80, 5 | proseWrap: 'never', 6 | semi: true, 7 | singleQuote: true, 8 | tabWidth: 2, 9 | trailingComma: 'es5', 10 | useTabs: false, 11 | }; 12 | -------------------------------------------------------------------------------- /packages/sporks-loader/src/preloader.js: -------------------------------------------------------------------------------- 1 | import { parse } from '@hs/sporks'; 2 | 3 | module.exports = function(content) { 4 | if (!this.enhancedLoadContext) { 5 | throw new Error('missing EnhancedLoaderPlugin'); 6 | } 7 | this.cacheable(); 8 | 9 | const { source, directives } = parse(content); 10 | 11 | this._module.meta = Object.assign({}, this._module.meta, { 12 | source, 13 | directives, 14 | }); 15 | 16 | return source; 17 | }; 18 | -------------------------------------------------------------------------------- /packages/webpack-enhanced-loader-plugin/src/webpack-compat/context-dependency.js: -------------------------------------------------------------------------------- 1 | import ContextDependency from 'webpack/lib/dependencies/ContextDependency'; 2 | 3 | function create(request, recursive, regExp, mode) { 4 | if (!(this.prototype !== ContextDependency.prototype)) { 5 | throw new Error('Cannot call `create` on non-ContextDependency'); 6 | } 7 | return new this({ request, recursive, regExp, mode }); 8 | } 9 | 10 | module.exports = { create }; 11 | -------------------------------------------------------------------------------- /packages/sporks/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hs/sporks", 3 | "version": "10.0.0", 4 | "license": "MIT", 5 | "homepage": "https://github.com/HubSpot/asset-bender/tree/master/packages/sporks", 6 | "repository": "github:HubSpot/asset-bender", 7 | "publishConfig": { 8 | "access": "public" 9 | }, 10 | "main": "lib", 11 | "dependencies": { 12 | "shellwords": "0.1.1" 13 | }, 14 | "scripts": { 15 | "build": "babel src -d lib --root-mode upward", 16 | "prepare": "yarn build --delete-dir-on-start" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/webpack-rule-composer/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hs/webpack-rule-composer", 3 | "version": "10.0.0", 4 | "license": "MIT", 5 | "homepage": "https://github.com/HubSpot/asset-bender/tree/master/packages/webpack-rule-composer", 6 | "repository": "github:HubSpot/asset-bender", 7 | "publishConfig": { 8 | "access": "public" 9 | }, 10 | "main": "lib", 11 | "scripts": { 12 | "test": "jest", 13 | "build": "babel src -d lib --root-mode upward", 14 | "prepare": "yarn build --delete-dir-on-start" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | # meow brings in a version of resolve which has intentionally malformed 3 | # package.json files that flow chokes on 4 | .*/resolve/test/.* 5 | 6 | [include] 7 | 8 | [libs] 9 | 10 | [options] 11 | suppress_comment= \\(.\\|\n\\)*\\$FlowIssue 12 | suppress_comment= \\(.\\|\n\\)*\\$FlowFixMe 13 | suppress_comment= \\(.\\|\n\\)*\\$FlowTestIgnore 14 | suppress_type=$FlowTODO 15 | suppress_type=$FlowTestIgnore 16 | module.name_mapper='^\(@hs/[^/]*\)/lib/\(.+\)$' -> '\1/src/\2' 17 | module.name_mapper='^\(@hs/[^/]*\)$' -> '\1/src' 18 | -------------------------------------------------------------------------------- /packages/webpack-dev-server-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hs/webpack-dev-server-plugin", 3 | "version": "10.0.0", 4 | "license": "MIT", 5 | "homepage": "https://github.com/HubSpot/asset-bender/tree/master/packages/webpack-dev-server-plugin", 6 | "repository": "github:HubSpot/asset-bender", 7 | "publishConfig": { 8 | "access": "public" 9 | }, 10 | "main": "lib/WebpackDevServerPlugin", 11 | "scripts": { 12 | "build": "babel src -d lib --root-mode upward", 13 | "prepare": "yarn build --delete-dir-on-start" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/webpack-enhanced-loader-plugin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hs/webpack-enhanced-loader-plugin", 3 | "version": "10.0.1", 4 | "license": "MIT", 5 | "homepage": "https://github.com/HubSpot/asset-bender/tree/master/packages/webpack-enhanced-loader-plugin", 6 | "repository": "github:HubSpot/asset-bender", 7 | "publishConfig": { 8 | "access": "public" 9 | }, 10 | "main": "lib/EnhancedLoaderPlugin", 11 | "peerDependencies": { 12 | "webpack": "3.x" 13 | }, 14 | "scripts": { 15 | "build": "babel src -d lib --root-mode upward", 16 | "prepare": "yarn build --delete-dir-on-start" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/sporks-loader/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hs/sporks-loader", 3 | "version": "10.0.0", 4 | "license": "MIT", 5 | "homepage": "https://github.com/HubSpot/asset-bender/tree/master/packages/sporks-loader", 6 | "repository": "github:HubSpot/asset-bender", 7 | "publishConfig": { 8 | "access": "public" 9 | }, 10 | "main": "lib", 11 | "dependencies": { 12 | "@hs/sporks": "10.0.0", 13 | "loader-utils": "^1.2.3", 14 | "webpack-sources": "^3.2.0" 15 | }, 16 | "peerDependencies": { 17 | "@hs/webpack-enhanced-loader-plugin": "*" 18 | }, 19 | "scripts": { 20 | "build": "babel src -d lib --root-mode upward", 21 | "prepare": "yarn build --delete-dir-on-start" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/webpack-graphql/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@hs/webpack-graphql", 3 | "version": "10.1.0", 4 | "license": "MIT", 5 | "homepage": "https://github.com/HubSpot/asset-bender/tree/master/packages/webpack-graphql", 6 | "repository": "github:HubSpot/asset-bender", 7 | "publishConfig": { 8 | "access": "public" 9 | }, 10 | "main": "lib/WebpackGraphQLPlugin", 11 | "dependencies": { 12 | "@hs/webpack-dev-server-plugin": "10.0.0", 13 | "express-graphql": "^0.6.12", 14 | "graphql": "^14.0.2", 15 | "graphql-tools": "^4.0.1" 16 | }, 17 | "scripts": { 18 | "build": "babel src -d lib --root-mode upward --copy-files", 19 | "prepare": "yarn build --delete-dir-on-start" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/webpack-dev-server-plugin/src/WebpackDevServerPlugin.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export default class WebpackDevServerPlugin { 4 | before: (app: any, server: any, compiler: any) => void; 5 | 6 | apply(compiler: any) { 7 | if (!compiler.options.devServer) { 8 | throw new Error( 9 | 'To use webpack-dev-server plugins, you must include a `devServer` option in your webpack config' 10 | ); 11 | } 12 | 13 | const originalBefore = compiler.options.devServer.before; 14 | 15 | compiler.options.devServer.before = (app, server) => { 16 | if (originalBefore) { 17 | originalBefore(app, server); 18 | } 19 | 20 | this.before(app, server, compiler); 21 | }; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/webpack-graphql/src/WebpackGraphQLPlugin.js: -------------------------------------------------------------------------------- 1 | import WebpackDevServerPlugin from '@hs/webpack-dev-server-plugin'; 2 | 3 | export default class WebpackGraphQLPlugin extends WebpackDevServerPlugin { 4 | constructor(opts) { 5 | super(); 6 | this.opts = opts; 7 | } 8 | 9 | before(app, server, compiler) { 10 | const { path, context, typeDefs, resolvers } = this.opts; 11 | const { buildContext, makeSchema } = require('./data'); 12 | const graphqlHTTP = require('express-graphql'); 13 | 14 | app.use( 15 | path, 16 | graphqlHTTP({ 17 | schema: makeSchema({ typeDefs, resolvers }), 18 | context: { ...buildContext(compiler), ...context }, 19 | graphiql: true, 20 | }) 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /packages/webpack-graphql/src/CompilerStatePlugin.js: -------------------------------------------------------------------------------- 1 | export default class CompilerStatePlugin { 2 | constructor() { 3 | this.resetPromise(); 4 | } 5 | 6 | resetPromise() { 7 | this.promise = new Promise(resolve => { 8 | this._resolve = resolve; 9 | }); 10 | } 11 | 12 | apply(compiler) { 13 | this.valid = false; 14 | 15 | compiler.hooks.done.tap('CompilerStatePlugin', ({ compilation }) => { 16 | this.valid = true; 17 | this._resolve(compilation); 18 | }); 19 | 20 | [ 21 | compiler.hooks.invalid, 22 | compiler.hooks.watchRun, 23 | compiler.hooks.run, 24 | ].forEach(hook => 25 | hook.tap('CompilerStatePlugin', () => { 26 | this.valid = false; 27 | this.resetPromise(); 28 | }) 29 | ); 30 | } 31 | 32 | then(...args) { 33 | return this.promise.then(...args); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | node-version: [12.x] 13 | 14 | steps: 15 | - uses: actions/checkout@v1 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | - name: install yarn 21 | run: | 22 | curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add - 23 | echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list 24 | sudo apt-get update 25 | sudo apt-get install yarn 26 | - name: yarn install, build, and test 27 | run: | 28 | yarn 29 | yarn build 30 | yarn test 31 | env: 32 | CI: true 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "devDependencies": { 4 | "@babel/cli": "^7.1.2", 5 | "@babel/core": "^7.1.2", 6 | "@babel/plugin-syntax-object-rest-spread": "^7.0.0", 7 | "@babel/plugin-transform-flow-strip-types": "^7.0.0", 8 | "@babel/plugin-transform-modules-commonjs": "^7.1.0", 9 | "babel-core": "^7.0.0-bridge.0", 10 | "babel-eslint": "^10.0.1", 11 | "babel-jest": "^23.6.0", 12 | "eslint": "4.17.0", 13 | "flow-bin": "^0.83.0", 14 | "flow-typed": "^2.5.1", 15 | "jest": "^23.6.0", 16 | "lerna": "4.0.0", 17 | "prettier": "^1.14.3" 18 | }, 19 | "workspaces": [ 20 | "packages/*" 21 | ], 22 | "scripts": { 23 | "build": "lerna run build", 24 | "build-watch": "lerna run build --parallel -- -- --watch", 25 | "lint": "eslint .", 26 | "prettier": "prettier '**/*.js' -l", 27 | "test": "yarn flow && yarn jest && yarn lint && yarn prettier" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 HubSpot, Inc. 2 | 3 | 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: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | 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. 8 | -------------------------------------------------------------------------------- /packages/webpack-rule-composer/__tests__/__snapshots__/index.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`getRuleComposer recursively resolves mixed strings and functions 1`] = ` 4 | Array [ 5 | Object { 6 | "test": /i-show-up-twice/, 7 | }, 8 | Object { 9 | "test": /i-show-up-twice/, 10 | }, 11 | Object { 12 | "test": /look-no-further/, 13 | }, 14 | ] 15 | `; 16 | 17 | exports[`getRuleComposer ruleComposer recursively resolves rule names to literal rules 1`] = ` 18 | Array [ 19 | Object { 20 | "test": /recursive-look-result/, 21 | }, 22 | Object { 23 | "test": /recursive-harder-result/, 24 | }, 25 | Object { 26 | "test": /look-no-further/, 27 | }, 28 | ] 29 | `; 30 | 31 | exports[`getRuleComposer ruleComposer resolves string rule names to literal rules 1`] = ` 32 | Array [ 33 | Object { 34 | "test": /simple-string-result/, 35 | }, 36 | ] 37 | `; 38 | 39 | exports[`getRuleComposer supports functions recursively 1`] = ` 40 | Array [ 41 | Object { 42 | "test": /recursive-function-result/, 43 | }, 44 | ] 45 | `; 46 | 47 | exports[`getRuleComposer supports functions that return literal rules 1`] = ` 48 | Array [ 49 | Object { 50 | "test": /simple-function-result/, 51 | }, 52 | ] 53 | `; 54 | -------------------------------------------------------------------------------- /packages/webpack-rule-composer/src/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | export type RuleRef = string; 4 | export type RuleFn = (...args: Array) => Rule; 5 | export type ConcreteRule = {}; 6 | 7 | export type AbstractRule = RuleRef | RuleFn; 8 | export type Rule = AbstractRule | Array; 9 | export type RuleResolver = (ruleName: string) => Rule; 10 | 11 | export default function getRuleComposer( 12 | ruleResolver: RuleResolver, 13 | ...args: Array 14 | ) { 15 | function composeRulesRec(rules: Rule): Array { 16 | if (typeof rules === 'string') { 17 | return composeRulesRec(ruleResolver(rules)); 18 | } else if (typeof rules === 'function') { 19 | return composeRulesRec(rules(...args)); 20 | } else if (Array.isArray(rules)) { 21 | return [].concat( 22 | ...rules.map(rule => { 23 | if (typeof rule === 'string') { 24 | return composeRulesRec(rule); 25 | } else if (typeof rule === 'function') { 26 | return composeRulesRec(rule(...args)); 27 | } else { 28 | return [rule]; 29 | } 30 | }) 31 | ); 32 | } 33 | throw new Error( 34 | `Rule is not an array or cannot be further resolved: ${JSON.stringify( 35 | rules, 36 | null, 37 | 2 38 | )}` 39 | ); 40 | } 41 | 42 | return (rule: AbstractRule): Array => { 43 | return composeRulesRec(rule); 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /packages/sporks/src/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import shellwords from 'shellwords'; 4 | 5 | export type Directive = { 6 | directive: string, 7 | args: Array, 8 | loc: { line: number, start: number }, 9 | source: string, 10 | }; 11 | 12 | export type ParseResult = { 13 | source: string, 14 | headerSource: string, 15 | directives: Array, 16 | }; 17 | 18 | // https://github.com/timmfin/sprockets-directive-loader/blob/3530b38ce7157989c65212f582eacfc04c803046/index.js#L11-L12 19 | 20 | function extractHeader(source: string): string { 21 | const headerPattern = /^(?:\s*((?:\/[*](?:\s*|.+?)*?[*]\/)|(?:###\n(?:[\s\S]*)\n###)|(?:\/\/.*\n?)+|(?:#.*\n?)+)*)*/m; 22 | 23 | const match = headerPattern.exec(source); 24 | // Must be at the very beginning of the file 25 | if (match && match.index === 0) { 26 | return match[0]; 27 | } else { 28 | return ''; 29 | } 30 | } 31 | 32 | export function parse(source: string): ParseResult { 33 | source = source.toString(); 34 | 35 | const headerSource = extractHeader(source); 36 | const bodySource = source.substr(headerSource.length); 37 | 38 | const directives = []; 39 | const headerLines = headerSource.split('\n').map((line, index) => { 40 | const match = line.match(/^(\W*=)\s*(\w+)\s*(.*?)(\*\/)?$/); 41 | if (!match) { 42 | return line; 43 | } 44 | const directive = match[2]; 45 | const args = shellwords.split(match[3]); 46 | directives.push({ 47 | directive, 48 | args, 49 | loc: { 50 | line: index + 1, 51 | // $FlowIssue https://github.com/facebook/flow/issues/3554 52 | start: match.index, 53 | }, 54 | source: match[0], 55 | }); 56 | return ''; 57 | }); 58 | 59 | return { 60 | source: headerLines.join('\n') + bodySource, 61 | headerSource, 62 | directives, 63 | }; 64 | } 65 | -------------------------------------------------------------------------------- /packages/sporks/__tests__/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import { parse } from '../src'; 4 | 5 | describe('parse', () => { 6 | it('parses #=', () => { 7 | expect(parse('#= hello world')).toMatchInlineSnapshot(` 8 | Object { 9 | "directives": Array [ 10 | Object { 11 | "args": Array [ 12 | "world", 13 | ], 14 | "directive": "hello", 15 | "loc": Object { 16 | "line": 1, 17 | "start": 0, 18 | }, 19 | "source": "#= hello world", 20 | }, 21 | ], 22 | "headerSource": "#= hello world", 23 | "source": "", 24 | } 25 | `); 26 | }); 27 | 28 | it('parses //=', () => { 29 | expect(parse('//= hello world')).toMatchInlineSnapshot(` 30 | Object { 31 | "directives": Array [ 32 | Object { 33 | "args": Array [ 34 | "world", 35 | ], 36 | "directive": "hello", 37 | "loc": Object { 38 | "line": 1, 39 | "start": 0, 40 | }, 41 | "source": "//= hello world", 42 | }, 43 | ], 44 | "headerSource": "//= hello world", 45 | "source": "", 46 | } 47 | `); 48 | }); 49 | 50 | it('parses multiple lines', () => { 51 | expect(parse('#= hello world\n#= hello mars\n#\n\n#= hello venus\n\n')) 52 | .toMatchInlineSnapshot(` 53 | Object { 54 | "directives": Array [ 55 | Object { 56 | "args": Array [ 57 | "world", 58 | ], 59 | "directive": "hello", 60 | "loc": Object { 61 | "line": 1, 62 | "start": 0, 63 | }, 64 | "source": "#= hello world", 65 | }, 66 | Object { 67 | "args": Array [ 68 | "mars", 69 | ], 70 | "directive": "hello", 71 | "loc": Object { 72 | "line": 2, 73 | "start": 0, 74 | }, 75 | "source": "#= hello mars", 76 | }, 77 | Object { 78 | "args": Array [ 79 | "venus", 80 | ], 81 | "directive": "hello", 82 | "loc": Object { 83 | "line": 5, 84 | "start": 0, 85 | }, 86 | "source": "#= hello venus", 87 | }, 88 | ], 89 | "headerSource": "#= hello world 90 | #= hello mars 91 | # 92 | 93 | #= hello venus 94 | 95 | ", 96 | "source": " 97 | 98 | # 99 | 100 | 101 | 102 | ", 103 | } 104 | `); 105 | }); 106 | }); 107 | -------------------------------------------------------------------------------- /packages/webpack-rule-composer/__tests__/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import type { RuleFn } from '../src'; 4 | import getRuleComposer from '../src'; 5 | 6 | describe('getRuleComposer', () => { 7 | const webpackOptions = {}; 8 | const argv = {}; 9 | const other = {}; 10 | const args = [webpackOptions, argv, other]; 11 | 12 | it('returns a function', () => { 13 | const ruleResolver = () => []; 14 | expect(typeof getRuleComposer(ruleResolver, ...args)).toBe('function'); 15 | }); 16 | 17 | describe('ruleComposer', () => { 18 | it('resolves string rule names to literal rules', () => { 19 | const ruleComposer = getRuleComposer( 20 | () => [{ test: /simple-string-result/ }], 21 | ...args 22 | ); 23 | expect(ruleComposer('string')).toMatchSnapshot(); 24 | }); 25 | 26 | it('recursively resolves rule names to literal rules', () => { 27 | const ruleComposer = getRuleComposer(ruleName => { 28 | if (ruleName === 'recursive-lookup') { 29 | return ['look', 'harder', { test: /look-no-further/ }]; 30 | } else if (ruleName === 'look') { 31 | return [{ test: /recursive-look-result/ }]; 32 | } else { 33 | return [{ test: /recursive-harder-result/ }]; 34 | } 35 | }, ...args); 36 | expect(ruleComposer('recursive-lookup')).toMatchSnapshot(); 37 | }); 38 | }); 39 | 40 | it('supports functions that return literal rules', () => { 41 | const ruleComposer = getRuleComposer(() => [], ...args); 42 | expect( 43 | ruleComposer(() => [{ test: /simple-function-result/ }]) 44 | ).toMatchSnapshot(); 45 | }); 46 | 47 | it('supports functions recursively', () => { 48 | const ruleComposer = getRuleComposer(() => [], ...args); 49 | expect( 50 | ruleComposer(() => () => [{ test: /recursive-function-result/ }]) 51 | ).toMatchSnapshot(); 52 | }); 53 | 54 | it('recursively resolves mixed strings and functions', () => { 55 | const ruleComposer = getRuleComposer(ruleName => { 56 | if (ruleName === 'initial') { 57 | return ['foo', (() => 'foo': RuleFn), { test: /look-no-further/ }]; 58 | } else if (ruleName === 'foo') { 59 | return [{ test: /i-show-up-twice/ }]; 60 | } else { 61 | return []; 62 | } 63 | }, ...args); 64 | expect(ruleComposer('initial')).toMatchSnapshot(); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /packages/webpack-graphql/src/schema.graphql: -------------------------------------------------------------------------------- 1 | scalar Raw 2 | 3 | # Why a module is included. The reverse of a dependency. 4 | type ModuleReason { 5 | module: Module 6 | dependency: Dependency 7 | } 8 | 9 | type Loader { 10 | loader: String! 11 | options: Raw 12 | } 13 | 14 | type Module { 15 | identifier: String! 16 | 17 | # MD5 of the identifier. 18 | identifierHash: String! 19 | buildTimestamp: Raw 20 | resource: String 21 | context: String 22 | id: String 23 | dependencies: [Dependency!]! 24 | reasons: [ModuleReason!]! 25 | issuer: Module 26 | issuerStack: [Module!]! 27 | source: Source 28 | loaders: [Loader!] 29 | 30 | errors: [Error!]! 31 | errorCount: Int! 32 | warnings: [Error!]! 33 | warningCount: Int! 34 | chunks: [Chunk!]! 35 | } 36 | 37 | type SourceLocationRange { 38 | start: SourceLocation! 39 | end: SourceLocation! 40 | } 41 | 42 | type SourceLocation { 43 | line: Int! 44 | column: Int! 45 | } 46 | 47 | type Dependency { 48 | request: String 49 | type: String! 50 | loc: SourceLocationRange 51 | module: Module 52 | } 53 | 54 | # Source, including sourcemap 55 | type Source { 56 | source: String 57 | size(limit: Int): Int! 58 | map: Raw 59 | } 60 | 61 | # A file generated by webpack 62 | type Asset { 63 | name: String! 64 | source: Source! 65 | } 66 | 67 | type ChunkOrigin { 68 | name: String! 69 | module: Module! 70 | } 71 | 72 | type Chunk { 73 | id: Int! 74 | ids: [Int!]! 75 | files: [String!]! 76 | name: String 77 | entrypoints: [Module!]! 78 | origins: [ChunkOrigin!]! 79 | modules: [Module!]! 80 | } 81 | 82 | type Entrypoint { 83 | name: String! 84 | chunks: [Chunk!]! 85 | } 86 | 87 | type Stats { 88 | json: Raw! 89 | string(useColors: Boolean): String! 90 | } 91 | 92 | type Error { 93 | name: String 94 | message: String! 95 | details: String 96 | stack: String 97 | module: Module 98 | } 99 | 100 | # The webpack compilation 101 | type Compilation { 102 | generation: Int! 103 | stats(preset: String): Stats! 104 | modules: [Module!]! 105 | module(identifier: String, identifierHash: String): Module 106 | assets: [Asset!]! 107 | asset(name: String): Asset 108 | chunks: [Chunk!]! 109 | entrypoints: [Entrypoint!]! 110 | children: [Compilation!]! 111 | fileDependencies: [String!]! 112 | contextDependencies: [String!]! 113 | entries: [Module!]! 114 | 115 | errors: [Error!]! 116 | errorCount: Int! 117 | warnings: [Error!]! 118 | warningCount: Int! 119 | } 120 | 121 | # The webpack compiler 122 | type Compiler { 123 | # Indicates that the compiler is valid, that is, not currently compiling 124 | valid: Boolean! 125 | 126 | # The most recent compilation. Waits for current compilation to finish. 127 | compilation: Compilation! 128 | } 129 | 130 | type Query { 131 | compiler: Compiler! 132 | } 133 | -------------------------------------------------------------------------------- /packages/webpack-enhanced-loader-plugin/src/EnhancedLoaderPlugin.js: -------------------------------------------------------------------------------- 1 | import LoaderDependency from 'webpack/lib/dependencies/LoaderDependency'; 2 | import ContextDependency from 'webpack/lib/dependencies/ContextDependency'; 3 | import LazySet from 'webpack/lib/util/LazySet'; 4 | 5 | import { create } from './webpack-compat/context-dependency'; 6 | 7 | class EnhancedLoaderContextDependency extends ContextDependency { 8 | get type() { 9 | return 'enhanced-loader-context'; 10 | } 11 | } 12 | EnhancedLoaderContextDependency.create = create; 13 | 14 | /** 15 | * Like the webpack LoaderPlugin, with promises and context support. 16 | */ 17 | module.exports = class EnhancedLoaderPlugin { 18 | apply(compiler) { 19 | compiler.hooks.compilation.tap( 20 | 'EnhancedLoaderPlugin', 21 | (compilation, { normalModuleFactory, contextModuleFactory }) => { 22 | compilation.dependencyFactories.set( 23 | LoaderDependency, 24 | normalModuleFactory 25 | ); 26 | compilation.dependencyFactories.set( 27 | EnhancedLoaderContextDependency, 28 | contextModuleFactory 29 | ); 30 | 31 | compilation.hooks.normalModuleLoader.tap( 32 | 'EnhancedLoaderPlugin', 33 | (loaderContext, loaderModule) => { 34 | loaderContext.enhancedLoadModule = function( 35 | request, 36 | module = loaderModule 37 | ) { 38 | const dep = new LoaderDependency(request); 39 | dep.loc = { 40 | name: request, 41 | }; 42 | return doLoad(this, dep, module); 43 | }; 44 | 45 | loaderContext.enhancedLoadContext = function( 46 | request, 47 | recursive, 48 | regExp, 49 | module = loaderModule 50 | ) { 51 | const dep = EnhancedLoaderContextDependency.create( 52 | request, 53 | recursive, 54 | regExp, 55 | 'sync' 56 | ); 57 | dep.loc = { 58 | name: request, 59 | }; 60 | return doLoad(this, dep, module); 61 | }; 62 | 63 | function doLoad(loaderContext, dep, module) { 64 | return new Promise((resolve, reject) => { 65 | compilation.buildQueue.increaseParallelism(); 66 | compilation.handleModuleCreation( 67 | { 68 | factory: compilation.dependencyFactories.get( 69 | dep.constructor 70 | ), 71 | dependencies: [dep], 72 | originModule: module, 73 | context: module.context, 74 | recursive: false, 75 | }, 76 | err => { 77 | compilation.buildQueue.decreaseParallelism(); 78 | if (err) { 79 | return reject(err); 80 | } 81 | const moduleGraph = compilation.moduleGraph; 82 | const depModule = moduleGraph.getModule(dep); 83 | 84 | if (!depModule) { 85 | return reject(new Error('Cannot load the module')); 86 | } 87 | if (depModule.building) { 88 | depModule.building.push(next); 89 | } else next(); 90 | 91 | function next(err) { 92 | if (err) return reject(err); 93 | 94 | if (depModule.error) return reject(depModule.error); 95 | 96 | const fileDependencies = new LazySet(); 97 | const contextDependencies = new LazySet(); 98 | const missingDependencies = new LazySet(); 99 | const buildDependencies = new LazySet(); 100 | depModule.addCacheDependencies( 101 | fileDependencies, 102 | contextDependencies, 103 | missingDependencies, 104 | buildDependencies 105 | ); 106 | 107 | for (const d of fileDependencies) { 108 | loaderContext.addDependency(d); 109 | } 110 | for (const d of contextDependencies) { 111 | loaderContext.addContextDependency(d); 112 | } 113 | for (const d of missingDependencies) { 114 | loaderContext.addMissingDependency(d); 115 | } 116 | for (const d of buildDependencies) { 117 | loaderContext.addBuildDependency(d); 118 | } 119 | return resolve(depModule); 120 | } 121 | } 122 | ); 123 | }); 124 | } 125 | } 126 | ); 127 | } 128 | ); 129 | } 130 | }; 131 | -------------------------------------------------------------------------------- /packages/webpack-graphql/src/data.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | 4 | import crypto from 'crypto'; 5 | import { makeExecutableSchema } from 'graphql-tools'; 6 | 7 | import CompilerStatePlugin from './CompilerStatePlugin'; 8 | 9 | function moduleIdentifierHash(mod) { 10 | if (!mod.meta) { 11 | mod.meta = {}; 12 | } 13 | if (!mod.meta.identifierHash) { 14 | mod.meta.identifierHash = crypto 15 | .createHash('md5') 16 | .update(mod.identifier()) 17 | .digest('hex'); 18 | } 19 | return mod.meta.identifierHash; 20 | } 21 | 22 | function getModule(compilation, identifierHash) { 23 | if (!compilation.modulesByIdentifierHash) { 24 | compilation.modulesByIdentifierHash = new Map( 25 | compilation.modules.map(mod => [moduleIdentifierHash(mod), mod]) 26 | ); 27 | } 28 | 29 | return compilation.modulesByIdentifierHash.get(identifierHash); 30 | } 31 | 32 | // returning an error directly is treated as an error 33 | // https://github.com/graphql/graphql-js/issues/591 34 | function unwrapError(e) { 35 | // Webpack allows us to register either error objects or strings as errors. 36 | if (typeof e === 'string') { 37 | return { 38 | message: e, 39 | }; 40 | } 41 | const { name, message, details, stack, module } = e; 42 | return { name, message, details, stack, module }; 43 | } 44 | 45 | // we only need resolvers for things that need to be serialized differently, 46 | // or for methods that take arguments 47 | const coreResolvers = { 48 | Stats: { 49 | json({ json }) { 50 | return json; 51 | }, 52 | 53 | string({ stats, json }, { useColors }) { 54 | return stats.constructor.jsonToString(json, useColors); 55 | }, 56 | }, 57 | 58 | Compilation: { 59 | stats(compilation, { preset }) { 60 | const stats = compilation.getStats(); 61 | return { stats, json: stats.toJson(preset) }; 62 | }, 63 | 64 | assets(compilation, _, context) { 65 | const assets = context.assetsByCompilation.get(compilation); 66 | return Object.keys(compilation.assets).map(name => ({ 67 | name, 68 | source: assets[name], 69 | })); 70 | }, 71 | 72 | asset(compilation, { name }, context) { 73 | const assets = context.assetsByCompilation.get(compilation); 74 | return { name, source: assets[name] }; 75 | }, 76 | 77 | module(compilation, { identifier, identifierHash }) { 78 | if (identifierHash) { 79 | return getModule(compilation, identifierHash); 80 | } 81 | return compilation._modules[identifier]; 82 | }, 83 | 84 | entrypoints(compilation) { 85 | return Object.keys(compilation.entrypoints).map( 86 | name => compilation.entrypoints[name] 87 | ); 88 | }, 89 | 90 | errorCount(compilation) { 91 | return compilation.getErrors().length; 92 | }, 93 | 94 | errors(compilation) { 95 | return compilation.getErrors().map(unwrapError); 96 | }, 97 | 98 | warningCount(compilation) { 99 | return compilation.getWarnings().length; 100 | }, 101 | 102 | warnings(compilation) { 103 | return compilation.getWarnings().map(unwrapError); 104 | }, 105 | }, 106 | 107 | Module: { 108 | identifierHash(module) { 109 | return moduleIdentifierHash(module); 110 | }, 111 | 112 | buildTimestamp(module) { 113 | return +module.buildTimestamp; 114 | }, 115 | 116 | source(module) { 117 | return module._source; 118 | }, 119 | 120 | issuerStack(module) { 121 | let current = module; 122 | const result = []; 123 | while (current.issuer) { 124 | current = current.issuer; 125 | result.push(current); 126 | } 127 | return result; 128 | }, 129 | 130 | errorCount(module) { 131 | return module.getNumberOfErrors(); 132 | }, 133 | 134 | errors(module) { 135 | const errors = module.getErrors(); 136 | return errors ? errors.map(unwrapError) : []; 137 | }, 138 | 139 | warningCount(module) { 140 | return module.getNumberOfWarnings(); 141 | }, 142 | 143 | warnings(module) { 144 | const warnings = module.getWarnings(); 145 | return warnings ? warnings.map(unwrapError) : []; 146 | }, 147 | 148 | chunks(module) { 149 | return module.getChunks(); 150 | }, 151 | 152 | reasons(module, _, context) { 153 | return context.compilerReady.then(compilation => { 154 | return Array.from( 155 | compilation.moduleGraph.getIncomingConnections(module) 156 | ); 157 | }); 158 | }, 159 | }, 160 | 161 | Chunk: { 162 | modules(chunk) { 163 | return chunk.getModules(); 164 | }, 165 | }, 166 | 167 | Raw: { 168 | __serialize(v) { 169 | return v; 170 | }, 171 | }, 172 | 173 | Query: { 174 | compiler(root, _, context) { 175 | const { compilerReady } = context; 176 | return { 177 | valid: compilerReady.valid, 178 | compilation: compilerReady, 179 | }; 180 | }, 181 | }, 182 | 183 | Dependency: { 184 | loc(dep) { 185 | // there are some weird locations, like strings 186 | return dep.loc && dep.loc.start ? dep.loc : null; 187 | }, 188 | 189 | module(dep, _, context) { 190 | return context.compilerReady.then(compilation => { 191 | return compilation.moduleGraph.getModule(dep); 192 | }); 193 | }, 194 | }, 195 | 196 | Source: { 197 | source(s) { 198 | const value = s.source(); 199 | return Buffer.isBuffer(value) ? null : value; 200 | }, 201 | }, 202 | }; 203 | 204 | export function buildContext(compiler) { 205 | const compilerReady = new CompilerStatePlugin(); 206 | // This is a hack to get actual assets before they are replaced with SizeOnlySource instances 207 | const assetsByCompilation = new WeakMap(); 208 | 209 | compilerReady.apply(compiler); 210 | 211 | const context = { compilerReady, compiler, assetsByCompilation }; 212 | 213 | compiler.hooks.compilation.tap('webpack-graphql', compilation => { 214 | context.compilation = compilation; 215 | if (compilation.generation) { 216 | compilation.generation++; 217 | } else { 218 | compilation.generation = 1; 219 | } 220 | }); 221 | 222 | compiler.hooks.watchRun.tapAsync('webpack-graphql', (watch, callback) => { 223 | context.watch = watch; 224 | callback(); 225 | }); 226 | 227 | compiler.hooks.assetEmitted.tap( 228 | 'webpack-graphql', 229 | (file, { compilation, source }) => { 230 | const assetsForCompilation = assetsByCompilation.get(compilation) || {}; 231 | assetsByCompilation.set(compilation, { 232 | ...assetsForCompilation, 233 | [file]: source, 234 | }); 235 | } 236 | ); 237 | 238 | return context; 239 | } 240 | 241 | export function makeSchema({ typeDefs, resolvers }) { 242 | const coreTypeDefs = fs.readFileSync( 243 | path.join(__dirname, 'schema.graphql'), 244 | 'utf8' 245 | ); 246 | 247 | return makeExecutableSchema({ 248 | typeDefs: [coreTypeDefs, ...typeDefs], 249 | resolvers: [coreResolvers, ...resolvers], 250 | }); 251 | } 252 | -------------------------------------------------------------------------------- /packages/sporks-loader/src/index.js: -------------------------------------------------------------------------------- 1 | /* @flow */ 2 | 3 | import type { ParseResult } from '@hs/sporks'; 4 | import path from 'path'; 5 | import Sources from 'webpack-sources'; 6 | import { getOptions } from 'loader-utils'; 7 | 8 | type WebpackModule = { 9 | meta: ParseResult & { sporksStacks?: Array }, 10 | dependencies: Array<{ request: string }>, 11 | context: string, 12 | request: string, 13 | resource: string, 14 | _source: any, 15 | }; 16 | 17 | type Handler = (...args: Array) => Promise>; 18 | 19 | type Handlers = { [directive: string]: Handler }; 20 | 21 | function parseResourcePath(resource) { 22 | const i = resource.indexOf('?'); 23 | if (i < 0) { 24 | return resource; 25 | } 26 | return resource.substr(0, i); 27 | } 28 | 29 | function withResourceQuery(request: string, query: string) { 30 | const parts = request.split('!'); 31 | const filepath = parts[parts.length - 1]; 32 | if (filepath.indexOf('?') > -1) { 33 | return `${request}&${query}`; 34 | } 35 | return `${request}?${query}`; 36 | } 37 | 38 | module.exports = function(source: string, sourceMap: any) { 39 | if (this._module.meta.source == null || !this._module.meta.directives) { 40 | throw new Error('missing sporks/preloader'); 41 | } 42 | 43 | const callback = this.async(); 44 | this.cacheable(); 45 | 46 | // FIXME document this option 47 | const options = getOptions(this) || {}; 48 | const resolveOptions = options.resolve || {}; 49 | const { types = {}, replaceExtensions = {} } = resolveOptions; 50 | 51 | const rootModule: WebpackModule = this._module; 52 | 53 | const extname = path.extname(this.resourcePath); 54 | const type = types[extname] || extname; 55 | 56 | const matchTypes = [type, ...(replaceExtensions[type] || [])]; 57 | 58 | const matchRe = new RegExp( 59 | `(${matchTypes.join('|').replace(/\./g, '\\.')})$` 60 | ); 61 | 62 | const processModule = async ( 63 | mod: WebpackModule 64 | ): Promise> => { 65 | if (mod.meta.source == null || !mod.meta.directives) { 66 | // something must have gone wrong in pitch 67 | throw new Error('missing sporks/preloader'); 68 | } 69 | const { directives }: ParseResult = mod.meta; 70 | 71 | let fromModule = mod; 72 | if (fromModule != rootModule) { 73 | fromModule = Object.create(rootModule); 74 | Object.assign(fromModule, { 75 | context: mod.context, 76 | request: mod.request, 77 | }); 78 | } 79 | 80 | const enhancedLoadModule = (request: string): Promise => { 81 | const sporksInternalRequest = withResourceQuery( 82 | request, 83 | 'sporks-internal' 84 | ); 85 | return this.enhancedLoadModule(sporksInternalRequest, fromModule); 86 | }; 87 | 88 | const enhancedLoadContext = async ( 89 | request: string, 90 | recursive: boolean, 91 | pattern?: string 92 | ): Promise> => { 93 | const patternRe = pattern ? new RegExp(pattern) : matchRe; 94 | const contextModule: WebpackModule = await this.enhancedLoadContext( 95 | request, 96 | recursive, 97 | patternRe, 98 | fromModule 99 | ); 100 | 101 | // FIXME for resons I don't uderstand, "context-module-factory" "alternatives" seem to produce duplicate results here 102 | const depPaths = Array.from( 103 | new Set( 104 | contextModule.dependencies.map(({ request }) => 105 | path.join(contextModule.context, request) 106 | ) 107 | ) 108 | ); 109 | 110 | return Promise.all( 111 | depPaths 112 | // exclude the root module 113 | // TODO why exclude the this.resourcePath and not mod.resourcePath? 114 | .filter(depPath => depPath != this.resourcePath) 115 | .map(depPath => enhancedLoadModule(depPath)) 116 | ); 117 | }; 118 | 119 | const parts = await Promise.all( 120 | directives.map(({ directive, args }) => { 121 | const handlers: Handlers = { 122 | require: p => { 123 | const extname = path.extname(p); 124 | if (extname === '') { 125 | p += type; 126 | } else if (extname !== type) { 127 | const pType = types[extname]; 128 | if (!pType) { 129 | // an extension we don't know about (possibly like file.min) 130 | p += type; 131 | } else if (pType === type) { 132 | p = p.slice(0, -extname.length) + type; 133 | } else { 134 | throw new Error(`${extname} can't be required from a ${type}`); 135 | } 136 | } 137 | return Promise.all([enhancedLoadModule(p)]); 138 | }, 139 | 140 | require_env: (...args) => { 141 | if (args.length > 0) { 142 | throw new Error( 143 | 'The require_env directive should take no parameters' 144 | ); 145 | } 146 | const { name, ext } = path.parse(parseResourcePath(mod.resource)); 147 | if (ext !== '.js') { 148 | throw new Error('require_env is only allowed in .js files'); 149 | } 150 | const env = options.sporksEnv || 'development'; 151 | return handlers.require(`./${name}.${env}.js`); 152 | }, 153 | 154 | require_tree: (path, pattern) => { 155 | return enhancedLoadContext(path, true, pattern); 156 | }, 157 | 158 | require_directory: (path, pattern) => { 159 | return enhancedLoadContext(path, false, pattern); 160 | }, 161 | 162 | require_lang: p => { 163 | if (p.indexOf('*') === -1) { 164 | throw new Error( 165 | `Cannot use require_lang without including the language wildcard ('*')` 166 | ); 167 | } 168 | const currentLang = path.basename( 169 | parseResourcePath(mod.resource), 170 | '.lyaml' 171 | ); 172 | return handlers.require(p.replace('*', currentLang)); 173 | }, 174 | }; 175 | 176 | const fn = handlers[directive]; 177 | if (!fn) { 178 | this.emitWarning(`Invalid directive ${directive}`); 179 | return []; 180 | } else { 181 | return fn(...args); 182 | } 183 | }) 184 | ); 185 | 186 | return [].concat(...parts); 187 | }; 188 | 189 | async function processModulesRecursive( 190 | mod: WebpackModule, 191 | stack = new Set() 192 | ): Promise> { 193 | if (stack.has(mod.resource)) { 194 | throw new Error( 195 | `circular requires:\n * ${Array.from([...stack, mod.resource]).join( 196 | '\n * ' 197 | )}` 198 | ); 199 | } 200 | if (!mod.meta) { 201 | // $FlowFixMe I don't think mod.meta can be missing: processModule would fail on missing directives 202 | mod.meta = {}; 203 | } 204 | if (!mod.meta.sporksStacks) { 205 | mod.meta.sporksStacks = []; 206 | } 207 | mod.meta.sporksStacks.push([...stack].reverse()); 208 | 209 | stack.add(mod.resource); 210 | 211 | let modules; 212 | try { 213 | modules = await processModule(mod); 214 | } catch (e) { 215 | if (e._processModulesRecursive) { 216 | throw e; 217 | } 218 | const err: any = new Error( 219 | `${e.message}\nWhile requiring ${mod.resource} in\n * ${Array.from( 220 | stack 221 | ).join('\n * ')}` 222 | ); 223 | err.hideStack = true; 224 | err._processModulesRecursive = stack; 225 | throw err; 226 | } 227 | 228 | const includedModules = await Promise.all( 229 | modules.map(m => processModulesRecursive(m, new Set(stack))) 230 | ); 231 | 232 | return [].concat(...includedModules, [mod]); 233 | } 234 | 235 | processModulesRecursive(rootModule) 236 | .then(modules => { 237 | // dedupe modules 238 | const uniqueModules = new Set(modules); 239 | 240 | const parts = [].concat( 241 | ...Array.from(uniqueModules, m => { 242 | let modSource; 243 | if (m === rootModule) { 244 | if (sourceMap) { 245 | modSource = new Sources.SourceMapSource( 246 | source, 247 | this.resourcePath, 248 | sourceMap 249 | ); 250 | } else { 251 | modSource = new Sources.OriginalSource(source, this.resourcePath); 252 | } 253 | } else { 254 | modSource = m._source; 255 | } 256 | 257 | return [modSource, '\n\n']; 258 | }) 259 | ); 260 | 261 | const result = new Sources.ConcatSource(...parts).sourceAndMap(); 262 | return result; 263 | }) 264 | .then( 265 | result => callback(null, result.source, result.map), 266 | err => callback(err) 267 | ) 268 | .catch(err => 269 | setImmediate(() => { 270 | throw err; 271 | }) 272 | ); 273 | }; 274 | -------------------------------------------------------------------------------- /flow-typed/npm/jest_v23.x.x.js: -------------------------------------------------------------------------------- 1 | // flow-typed signature: dc105d04239dd6b5d964507b6bddd0ec 2 | // flow-typed version: 6c2ed501b8/jest_v23.x.x/flow_>=v0.39.x 3 | 4 | type JestMockFn, TReturn> = { 5 | (...args: TArguments): TReturn, 6 | /** 7 | * An object for introspecting mock calls 8 | */ 9 | mock: { 10 | /** 11 | * An array that represents all calls that have been made into this mock 12 | * function. Each call is represented by an array of arguments that were 13 | * passed during the call. 14 | */ 15 | calls: Array, 16 | /** 17 | * An array that contains all the object instances that have been 18 | * instantiated from this mock function. 19 | */ 20 | instances: Array, 21 | /** 22 | * An array that contains all the object results that have been 23 | * returned by this mock function call 24 | */ 25 | results: Array<{ isThrow: boolean, value: TReturn }> 26 | }, 27 | /** 28 | * Resets all information stored in the mockFn.mock.calls and 29 | * mockFn.mock.instances arrays. Often this is useful when you want to clean 30 | * up a mock's usage data between two assertions. 31 | */ 32 | mockClear(): void, 33 | /** 34 | * Resets all information stored in the mock. This is useful when you want to 35 | * completely restore a mock back to its initial state. 36 | */ 37 | mockReset(): void, 38 | /** 39 | * Removes the mock and restores the initial implementation. This is useful 40 | * when you want to mock functions in certain test cases and restore the 41 | * original implementation in others. Beware that mockFn.mockRestore only 42 | * works when mock was created with jest.spyOn. Thus you have to take care of 43 | * restoration yourself when manually assigning jest.fn(). 44 | */ 45 | mockRestore(): void, 46 | /** 47 | * Accepts a function that should be used as the implementation of the mock. 48 | * The mock itself will still record all calls that go into and instances 49 | * that come from itself -- the only difference is that the implementation 50 | * will also be executed when the mock is called. 51 | */ 52 | mockImplementation( 53 | fn: (...args: TArguments) => TReturn 54 | ): JestMockFn, 55 | /** 56 | * Accepts a function that will be used as an implementation of the mock for 57 | * one call to the mocked function. Can be chained so that multiple function 58 | * calls produce different results. 59 | */ 60 | mockImplementationOnce( 61 | fn: (...args: TArguments) => TReturn 62 | ): JestMockFn, 63 | /** 64 | * Accepts a string to use in test result output in place of "jest.fn()" to 65 | * indicate which mock function is being referenced. 66 | */ 67 | mockName(name: string): JestMockFn, 68 | /** 69 | * Just a simple sugar function for returning `this` 70 | */ 71 | mockReturnThis(): void, 72 | /** 73 | * Accepts a value that will be returned whenever the mock function is called. 74 | */ 75 | mockReturnValue(value: TReturn): JestMockFn, 76 | /** 77 | * Sugar for only returning a value once inside your mock 78 | */ 79 | mockReturnValueOnce(value: TReturn): JestMockFn, 80 | /** 81 | * Sugar for jest.fn().mockImplementation(() => Promise.resolve(value)) 82 | */ 83 | mockResolvedValue(value: TReturn): JestMockFn>, 84 | /** 85 | * Sugar for jest.fn().mockImplementationOnce(() => Promise.resolve(value)) 86 | */ 87 | mockResolvedValueOnce(value: TReturn): JestMockFn>, 88 | /** 89 | * Sugar for jest.fn().mockImplementation(() => Promise.reject(value)) 90 | */ 91 | mockRejectedValue(value: TReturn): JestMockFn>, 92 | /** 93 | * Sugar for jest.fn().mockImplementationOnce(() => Promise.reject(value)) 94 | */ 95 | mockRejectedValueOnce(value: TReturn): JestMockFn> 96 | }; 97 | 98 | type JestAsymmetricEqualityType = { 99 | /** 100 | * A custom Jasmine equality tester 101 | */ 102 | asymmetricMatch(value: mixed): boolean 103 | }; 104 | 105 | type JestCallsType = { 106 | allArgs(): mixed, 107 | all(): mixed, 108 | any(): boolean, 109 | count(): number, 110 | first(): mixed, 111 | mostRecent(): mixed, 112 | reset(): void 113 | }; 114 | 115 | type JestClockType = { 116 | install(): void, 117 | mockDate(date: Date): void, 118 | tick(milliseconds?: number): void, 119 | uninstall(): void 120 | }; 121 | 122 | type JestMatcherResult = { 123 | message?: string | (() => string), 124 | pass: boolean 125 | }; 126 | 127 | type JestMatcher = (actual: any, expected: any) => 128 | | JestMatcherResult 129 | | Promise; 130 | 131 | type JestPromiseType = { 132 | /** 133 | * Use rejects to unwrap the reason of a rejected promise so any other 134 | * matcher can be chained. If the promise is fulfilled the assertion fails. 135 | */ 136 | rejects: JestExpectType, 137 | /** 138 | * Use resolves to unwrap the value of a fulfilled promise so any other 139 | * matcher can be chained. If the promise is rejected the assertion fails. 140 | */ 141 | resolves: JestExpectType 142 | }; 143 | 144 | /** 145 | * Jest allows functions and classes to be used as test names in test() and 146 | * describe() 147 | */ 148 | type JestTestName = string | Function; 149 | 150 | /** 151 | * Plugin: jest-styled-components 152 | */ 153 | 154 | type JestStyledComponentsMatcherValue = 155 | | string 156 | | JestAsymmetricEqualityType 157 | | RegExp 158 | | typeof undefined; 159 | 160 | type JestStyledComponentsMatcherOptions = { 161 | media?: string; 162 | modifier?: string; 163 | supports?: string; 164 | } 165 | 166 | type JestStyledComponentsMatchersType = { 167 | toHaveStyleRule( 168 | property: string, 169 | value: JestStyledComponentsMatcherValue, 170 | options?: JestStyledComponentsMatcherOptions 171 | ): void, 172 | }; 173 | 174 | /** 175 | * Plugin: jest-enzyme 176 | */ 177 | type EnzymeMatchersType = { 178 | // 5.x 179 | toBeEmpty(): void, 180 | toBePresent(): void, 181 | // 6.x 182 | toBeChecked(): void, 183 | toBeDisabled(): void, 184 | toBeEmptyRender(): void, 185 | toContainMatchingElement(selector: string): void; 186 | toContainMatchingElements(n: number, selector: string): void; 187 | toContainExactlyOneMatchingElement(selector: string): void; 188 | toContainReact(element: React$Element): void, 189 | toExist(): void, 190 | toHaveClassName(className: string): void, 191 | toHaveHTML(html: string): void, 192 | toHaveProp: ((propKey: string, propValue?: any) => void) & ((props: Object) => void), 193 | toHaveRef(refName: string): void, 194 | toHaveState: ((stateKey: string, stateValue?: any) => void) & ((state: Object) => void), 195 | toHaveStyle: ((styleKey: string, styleValue?: any) => void) & ((style: Object) => void), 196 | toHaveTagName(tagName: string): void, 197 | toHaveText(text: string): void, 198 | toHaveValue(value: any): void, 199 | toIncludeText(text: string): void, 200 | toMatchElement( 201 | element: React$Element, 202 | options?: {| ignoreProps?: boolean, verbose?: boolean |}, 203 | ): void, 204 | toMatchSelector(selector: string): void, 205 | // 7.x 206 | toHaveDisplayName(name: string): void, 207 | }; 208 | 209 | // DOM testing library extensions https://github.com/kentcdodds/dom-testing-library#custom-jest-matchers 210 | type DomTestingLibraryType = { 211 | toBeInTheDOM(): void, 212 | toHaveTextContent(content: string): void, 213 | toHaveAttribute(name: string, expectedValue?: string): void 214 | }; 215 | 216 | // Jest JQuery Matchers: https://github.com/unindented/custom-jquery-matchers 217 | type JestJQueryMatchersType = { 218 | toExist(): void, 219 | toHaveLength(len: number): void, 220 | toHaveId(id: string): void, 221 | toHaveClass(className: string): void, 222 | toHaveTag(tag: string): void, 223 | toHaveAttr(key: string, val?: any): void, 224 | toHaveProp(key: string, val?: any): void, 225 | toHaveText(text: string | RegExp): void, 226 | toHaveData(key: string, val?: any): void, 227 | toHaveValue(val: any): void, 228 | toHaveCss(css: {[key: string]: any}): void, 229 | toBeChecked(): void, 230 | toBeDisabled(): void, 231 | toBeEmpty(): void, 232 | toBeHidden(): void, 233 | toBeSelected(): void, 234 | toBeVisible(): void, 235 | toBeFocused(): void, 236 | toBeInDom(): void, 237 | toBeMatchedBy(sel: string): void, 238 | toHaveDescendant(sel: string): void, 239 | toHaveDescendantWithText(sel: string, text: string | RegExp): void 240 | }; 241 | 242 | 243 | // Jest Extended Matchers: https://github.com/jest-community/jest-extended 244 | type JestExtendedMatchersType = { 245 | /** 246 | * Note: Currently unimplemented 247 | * Passing assertion 248 | * 249 | * @param {String} message 250 | */ 251 | // pass(message: string): void; 252 | 253 | /** 254 | * Note: Currently unimplemented 255 | * Failing assertion 256 | * 257 | * @param {String} message 258 | */ 259 | // fail(message: string): void; 260 | 261 | /** 262 | * Use .toBeEmpty when checking if a String '', Array [] or Object {} is empty. 263 | */ 264 | toBeEmpty(): void; 265 | 266 | /** 267 | * Use .toBeOneOf when checking if a value is a member of a given Array. 268 | * @param {Array.<*>} members 269 | */ 270 | toBeOneOf(members: any[]): void; 271 | 272 | /** 273 | * Use `.toBeNil` when checking a value is `null` or `undefined`. 274 | */ 275 | toBeNil(): void; 276 | 277 | /** 278 | * Use `.toSatisfy` when you want to use a custom matcher by supplying a predicate function that returns a `Boolean`. 279 | * @param {Function} predicate 280 | */ 281 | toSatisfy(predicate: (n: any) => boolean): void; 282 | 283 | /** 284 | * Use `.toBeArray` when checking if a value is an `Array`. 285 | */ 286 | toBeArray(): void; 287 | 288 | /** 289 | * Use `.toBeArrayOfSize` when checking if a value is an `Array` of size x. 290 | * @param {Number} x 291 | */ 292 | toBeArrayOfSize(x: number): void; 293 | 294 | /** 295 | * Use `.toIncludeAllMembers` when checking if an `Array` contains all of the same members of a given set. 296 | * @param {Array.<*>} members 297 | */ 298 | toIncludeAllMembers(members: any[]): void; 299 | 300 | /** 301 | * Use `.toIncludeAnyMembers` when checking if an `Array` contains any of the members of a given set. 302 | * @param {Array.<*>} members 303 | */ 304 | toIncludeAnyMembers(members: any[]): void; 305 | 306 | /** 307 | * Use `.toSatisfyAll` when you want to use a custom matcher by supplying a predicate function that returns a `Boolean` for all values in an array. 308 | * @param {Function} predicate 309 | */ 310 | toSatisfyAll(predicate: (n: any) => boolean): void; 311 | 312 | /** 313 | * Use `.toBeBoolean` when checking if a value is a `Boolean`. 314 | */ 315 | toBeBoolean(): void; 316 | 317 | /** 318 | * Use `.toBeTrue` when checking a value is equal (===) to `true`. 319 | */ 320 | toBeTrue(): void; 321 | 322 | /** 323 | * Use `.toBeFalse` when checking a value is equal (===) to `false`. 324 | */ 325 | toBeFalse(): void; 326 | 327 | /** 328 | * Use .toBeDate when checking if a value is a Date. 329 | */ 330 | toBeDate(): void; 331 | 332 | /** 333 | * Use `.toBeFunction` when checking if a value is a `Function`. 334 | */ 335 | toBeFunction(): void; 336 | 337 | /** 338 | * Use `.toHaveBeenCalledBefore` when checking if a `Mock` was called before another `Mock`. 339 | * 340 | * Note: Required Jest version >22 341 | * Note: Your mock functions will have to be asynchronous to cause the timestamps inside of Jest to occur in a differentJS event loop, otherwise the mock timestamps will all be the same 342 | * 343 | * @param {Mock} mock 344 | */ 345 | toHaveBeenCalledBefore(mock: JestMockFn): void; 346 | 347 | /** 348 | * Use `.toBeNumber` when checking if a value is a `Number`. 349 | */ 350 | toBeNumber(): void; 351 | 352 | /** 353 | * Use `.toBeNaN` when checking a value is `NaN`. 354 | */ 355 | toBeNaN(): void; 356 | 357 | /** 358 | * Use `.toBeFinite` when checking if a value is a `Number`, not `NaN` or `Infinity`. 359 | */ 360 | toBeFinite(): void; 361 | 362 | /** 363 | * Use `.toBePositive` when checking if a value is a positive `Number`. 364 | */ 365 | toBePositive(): void; 366 | 367 | /** 368 | * Use `.toBeNegative` when checking if a value is a negative `Number`. 369 | */ 370 | toBeNegative(): void; 371 | 372 | /** 373 | * Use `.toBeEven` when checking if a value is an even `Number`. 374 | */ 375 | toBeEven(): void; 376 | 377 | /** 378 | * Use `.toBeOdd` when checking if a value is an odd `Number`. 379 | */ 380 | toBeOdd(): void; 381 | 382 | /** 383 | * Use `.toBeWithin` when checking if a number is in between the given bounds of: start (inclusive) and end (exclusive). 384 | * 385 | * @param {Number} start 386 | * @param {Number} end 387 | */ 388 | toBeWithin(start: number, end: number): void; 389 | 390 | /** 391 | * Use `.toBeObject` when checking if a value is an `Object`. 392 | */ 393 | toBeObject(): void; 394 | 395 | /** 396 | * Use `.toContainKey` when checking if an object contains the provided key. 397 | * 398 | * @param {String} key 399 | */ 400 | toContainKey(key: string): void; 401 | 402 | /** 403 | * Use `.toContainKeys` when checking if an object has all of the provided keys. 404 | * 405 | * @param {Array.} keys 406 | */ 407 | toContainKeys(keys: string[]): void; 408 | 409 | /** 410 | * Use `.toContainAllKeys` when checking if an object only contains all of the provided keys. 411 | * 412 | * @param {Array.} keys 413 | */ 414 | toContainAllKeys(keys: string[]): void; 415 | 416 | /** 417 | * Use `.toContainAnyKeys` when checking if an object contains at least one of the provided keys. 418 | * 419 | * @param {Array.} keys 420 | */ 421 | toContainAnyKeys(keys: string[]): void; 422 | 423 | /** 424 | * Use `.toContainValue` when checking if an object contains the provided value. 425 | * 426 | * @param {*} value 427 | */ 428 | toContainValue(value: any): void; 429 | 430 | /** 431 | * Use `.toContainValues` when checking if an object contains all of the provided values. 432 | * 433 | * @param {Array.<*>} values 434 | */ 435 | toContainValues(values: any[]): void; 436 | 437 | /** 438 | * Use `.toContainAllValues` when checking if an object only contains all of the provided values. 439 | * 440 | * @param {Array.<*>} values 441 | */ 442 | toContainAllValues(values: any[]): void; 443 | 444 | /** 445 | * Use `.toContainAnyValues` when checking if an object contains at least one of the provided values. 446 | * 447 | * @param {Array.<*>} values 448 | */ 449 | toContainAnyValues(values: any[]): void; 450 | 451 | /** 452 | * Use `.toContainEntry` when checking if an object contains the provided entry. 453 | * 454 | * @param {Array.} entry 455 | */ 456 | toContainEntry(entry: [string, string]): void; 457 | 458 | /** 459 | * Use `.toContainEntries` when checking if an object contains all of the provided entries. 460 | * 461 | * @param {Array.>} entries 462 | */ 463 | toContainEntries(entries: [string, string][]): void; 464 | 465 | /** 466 | * Use `.toContainAllEntries` when checking if an object only contains all of the provided entries. 467 | * 468 | * @param {Array.>} entries 469 | */ 470 | toContainAllEntries(entries: [string, string][]): void; 471 | 472 | /** 473 | * Use `.toContainAnyEntries` when checking if an object contains at least one of the provided entries. 474 | * 475 | * @param {Array.>} entries 476 | */ 477 | toContainAnyEntries(entries: [string, string][]): void; 478 | 479 | /** 480 | * Use `.toBeExtensible` when checking if an object is extensible. 481 | */ 482 | toBeExtensible(): void; 483 | 484 | /** 485 | * Use `.toBeFrozen` when checking if an object is frozen. 486 | */ 487 | toBeFrozen(): void; 488 | 489 | /** 490 | * Use `.toBeSealed` when checking if an object is sealed. 491 | */ 492 | toBeSealed(): void; 493 | 494 | /** 495 | * Use `.toBeString` when checking if a value is a `String`. 496 | */ 497 | toBeString(): void; 498 | 499 | /** 500 | * Use `.toEqualCaseInsensitive` when checking if a string is equal (===) to another ignoring the casing of both strings. 501 | * 502 | * @param {String} string 503 | */ 504 | toEqualCaseInsensitive(string: string): void; 505 | 506 | /** 507 | * Use `.toStartWith` when checking if a `String` starts with a given `String` prefix. 508 | * 509 | * @param {String} prefix 510 | */ 511 | toStartWith(prefix: string): void; 512 | 513 | /** 514 | * Use `.toEndWith` when checking if a `String` ends with a given `String` suffix. 515 | * 516 | * @param {String} suffix 517 | */ 518 | toEndWith(suffix: string): void; 519 | 520 | /** 521 | * Use `.toInclude` when checking if a `String` includes the given `String` substring. 522 | * 523 | * @param {String} substring 524 | */ 525 | toInclude(substring: string): void; 526 | 527 | /** 528 | * Use `.toIncludeRepeated` when checking if a `String` includes the given `String` substring the correct number of times. 529 | * 530 | * @param {String} substring 531 | * @param {Number} times 532 | */ 533 | toIncludeRepeated(substring: string, times: number): void; 534 | 535 | /** 536 | * Use `.toIncludeMultiple` when checking if a `String` includes all of the given substrings. 537 | * 538 | * @param {Array.} substring 539 | */ 540 | toIncludeMultiple(substring: string[]): void; 541 | }; 542 | 543 | interface JestExpectType { 544 | not: 545 | & JestExpectType 546 | & EnzymeMatchersType 547 | & DomTestingLibraryType 548 | & JestJQueryMatchersType 549 | & JestStyledComponentsMatchersType 550 | & JestExtendedMatchersType, 551 | /** 552 | * If you have a mock function, you can use .lastCalledWith to test what 553 | * arguments it was last called with. 554 | */ 555 | lastCalledWith(...args: Array): void, 556 | /** 557 | * toBe just checks that a value is what you expect. It uses === to check 558 | * strict equality. 559 | */ 560 | toBe(value: any): void, 561 | /** 562 | * Use .toBeCalledWith to ensure that a mock function was called with 563 | * specific arguments. 564 | */ 565 | toBeCalledWith(...args: Array): void, 566 | /** 567 | * Using exact equality with floating point numbers is a bad idea. Rounding 568 | * means that intuitive things fail. 569 | */ 570 | toBeCloseTo(num: number, delta: any): void, 571 | /** 572 | * Use .toBeDefined to check that a variable is not undefined. 573 | */ 574 | toBeDefined(): void, 575 | /** 576 | * Use .toBeFalsy when you don't care what a value is, you just want to 577 | * ensure a value is false in a boolean context. 578 | */ 579 | toBeFalsy(): void, 580 | /** 581 | * To compare floating point numbers, you can use toBeGreaterThan. 582 | */ 583 | toBeGreaterThan(number: number): void, 584 | /** 585 | * To compare floating point numbers, you can use toBeGreaterThanOrEqual. 586 | */ 587 | toBeGreaterThanOrEqual(number: number): void, 588 | /** 589 | * To compare floating point numbers, you can use toBeLessThan. 590 | */ 591 | toBeLessThan(number: number): void, 592 | /** 593 | * To compare floating point numbers, you can use toBeLessThanOrEqual. 594 | */ 595 | toBeLessThanOrEqual(number: number): void, 596 | /** 597 | * Use .toBeInstanceOf(Class) to check that an object is an instance of a 598 | * class. 599 | */ 600 | toBeInstanceOf(cls: Class<*>): void, 601 | /** 602 | * .toBeNull() is the same as .toBe(null) but the error messages are a bit 603 | * nicer. 604 | */ 605 | toBeNull(): void, 606 | /** 607 | * Use .toBeTruthy when you don't care what a value is, you just want to 608 | * ensure a value is true in a boolean context. 609 | */ 610 | toBeTruthy(): void, 611 | /** 612 | * Use .toBeUndefined to check that a variable is undefined. 613 | */ 614 | toBeUndefined(): void, 615 | /** 616 | * Use .toContain when you want to check that an item is in a list. For 617 | * testing the items in the list, this uses ===, a strict equality check. 618 | */ 619 | toContain(item: any): void, 620 | /** 621 | * Use .toContainEqual when you want to check that an item is in a list. For 622 | * testing the items in the list, this matcher recursively checks the 623 | * equality of all fields, rather than checking for object identity. 624 | */ 625 | toContainEqual(item: any): void, 626 | /** 627 | * Use .toEqual when you want to check that two objects have the same value. 628 | * This matcher recursively checks the equality of all fields, rather than 629 | * checking for object identity. 630 | */ 631 | toEqual(value: any): void, 632 | /** 633 | * Use .toHaveBeenCalled to ensure that a mock function got called. 634 | */ 635 | toHaveBeenCalled(): void, 636 | toBeCalled(): void; 637 | /** 638 | * Use .toHaveBeenCalledTimes to ensure that a mock function got called exact 639 | * number of times. 640 | */ 641 | toHaveBeenCalledTimes(number: number): void, 642 | toBeCalledTimes(number: number): void; 643 | /** 644 | * 645 | */ 646 | toHaveBeenNthCalledWith(nthCall: number, ...args: Array): void; 647 | nthCalledWith(nthCall: number, ...args: Array): void; 648 | /** 649 | * 650 | */ 651 | toHaveReturned(): void; 652 | toReturn(): void; 653 | /** 654 | * 655 | */ 656 | toHaveReturnedTimes(number: number): void; 657 | toReturnTimes(number: number): void; 658 | /** 659 | * 660 | */ 661 | toHaveReturnedWith(value: any): void; 662 | toReturnWith(value: any): void; 663 | /** 664 | * 665 | */ 666 | toHaveLastReturnedWith(value: any): void; 667 | lastReturnedWith(value: any): void; 668 | /** 669 | * 670 | */ 671 | toHaveNthReturnedWith(nthCall: number, value: any): void; 672 | nthReturnedWith(nthCall: number, value: any): void; 673 | /** 674 | * Use .toHaveBeenCalledWith to ensure that a mock function was called with 675 | * specific arguments. 676 | */ 677 | toHaveBeenCalledWith(...args: Array): void, 678 | toBeCalledWith(...args: Array): void, 679 | /** 680 | * Use .toHaveBeenLastCalledWith to ensure that a mock function was last called 681 | * with specific arguments. 682 | */ 683 | toHaveBeenLastCalledWith(...args: Array): void, 684 | lastCalledWith(...args: Array): void, 685 | /** 686 | * Check that an object has a .length property and it is set to a certain 687 | * numeric value. 688 | */ 689 | toHaveLength(number: number): void, 690 | /** 691 | * 692 | */ 693 | toHaveProperty(propPath: string, value?: any): void, 694 | /** 695 | * Use .toMatch to check that a string matches a regular expression or string. 696 | */ 697 | toMatch(regexpOrString: RegExp | string): void, 698 | /** 699 | * Use .toMatchObject to check that a javascript object matches a subset of the properties of an object. 700 | */ 701 | toMatchObject(object: Object | Array): void, 702 | /** 703 | * Use .toStrictEqual to check that a javascript object matches a subset of the properties of an object. 704 | */ 705 | toStrictEqual(value: any): void, 706 | /** 707 | * This ensures that an Object matches the most recent snapshot. 708 | */ 709 | toMatchSnapshot(propertyMatchers?: any, name?: string): void, 710 | /** 711 | * This ensures that an Object matches the most recent snapshot. 712 | */ 713 | toMatchSnapshot(name: string): void, 714 | 715 | toMatchInlineSnapshot(snapshot?: string): void, 716 | toMatchInlineSnapshot(propertyMatchers?: any, snapshot?: string): void, 717 | /** 718 | * Use .toThrow to test that a function throws when it is called. 719 | * If you want to test that a specific error gets thrown, you can provide an 720 | * argument to toThrow. The argument can be a string for the error message, 721 | * a class for the error, or a regex that should match the error. 722 | * 723 | * Alias: .toThrowError 724 | */ 725 | toThrow(message?: string | Error | Class | RegExp): void, 726 | toThrowError(message?: string | Error | Class | RegExp): void, 727 | /** 728 | * Use .toThrowErrorMatchingSnapshot to test that a function throws a error 729 | * matching the most recent snapshot when it is called. 730 | */ 731 | toThrowErrorMatchingSnapshot(): void, 732 | toThrowErrorMatchingInlineSnapshot(snapshot?: string): void, 733 | } 734 | 735 | type JestObjectType = { 736 | /** 737 | * Disables automatic mocking in the module loader. 738 | * 739 | * After this method is called, all `require()`s will return the real 740 | * versions of each module (rather than a mocked version). 741 | */ 742 | disableAutomock(): JestObjectType, 743 | /** 744 | * An un-hoisted version of disableAutomock 745 | */ 746 | autoMockOff(): JestObjectType, 747 | /** 748 | * Enables automatic mocking in the module loader. 749 | */ 750 | enableAutomock(): JestObjectType, 751 | /** 752 | * An un-hoisted version of enableAutomock 753 | */ 754 | autoMockOn(): JestObjectType, 755 | /** 756 | * Clears the mock.calls and mock.instances properties of all mocks. 757 | * Equivalent to calling .mockClear() on every mocked function. 758 | */ 759 | clearAllMocks(): JestObjectType, 760 | /** 761 | * Resets the state of all mocks. Equivalent to calling .mockReset() on every 762 | * mocked function. 763 | */ 764 | resetAllMocks(): JestObjectType, 765 | /** 766 | * Restores all mocks back to their original value. 767 | */ 768 | restoreAllMocks(): JestObjectType, 769 | /** 770 | * Removes any pending timers from the timer system. 771 | */ 772 | clearAllTimers(): void, 773 | /** 774 | * The same as `mock` but not moved to the top of the expectation by 775 | * babel-jest. 776 | */ 777 | doMock(moduleName: string, moduleFactory?: any): JestObjectType, 778 | /** 779 | * The same as `unmock` but not moved to the top of the expectation by 780 | * babel-jest. 781 | */ 782 | dontMock(moduleName: string): JestObjectType, 783 | /** 784 | * Returns a new, unused mock function. Optionally takes a mock 785 | * implementation. 786 | */ 787 | fn, TReturn>( 788 | implementation?: (...args: TArguments) => TReturn 789 | ): JestMockFn, 790 | /** 791 | * Determines if the given function is a mocked function. 792 | */ 793 | isMockFunction(fn: Function): boolean, 794 | /** 795 | * Given the name of a module, use the automatic mocking system to generate a 796 | * mocked version of the module for you. 797 | */ 798 | genMockFromModule(moduleName: string): any, 799 | /** 800 | * Mocks a module with an auto-mocked version when it is being required. 801 | * 802 | * The second argument can be used to specify an explicit module factory that 803 | * is being run instead of using Jest's automocking feature. 804 | * 805 | * The third argument can be used to create virtual mocks -- mocks of modules 806 | * that don't exist anywhere in the system. 807 | */ 808 | mock( 809 | moduleName: string, 810 | moduleFactory?: any, 811 | options?: Object 812 | ): JestObjectType, 813 | /** 814 | * Returns the actual module instead of a mock, bypassing all checks on 815 | * whether the module should receive a mock implementation or not. 816 | */ 817 | requireActual(moduleName: string): any, 818 | /** 819 | * Returns a mock module instead of the actual module, bypassing all checks 820 | * on whether the module should be required normally or not. 821 | */ 822 | requireMock(moduleName: string): any, 823 | /** 824 | * Resets the module registry - the cache of all required modules. This is 825 | * useful to isolate modules where local state might conflict between tests. 826 | */ 827 | resetModules(): JestObjectType, 828 | /** 829 | * Exhausts the micro-task queue (usually interfaced in node via 830 | * process.nextTick). 831 | */ 832 | runAllTicks(): void, 833 | /** 834 | * Exhausts the macro-task queue (i.e., all tasks queued by setTimeout(), 835 | * setInterval(), and setImmediate()). 836 | */ 837 | runAllTimers(): void, 838 | /** 839 | * Exhausts all tasks queued by setImmediate(). 840 | */ 841 | runAllImmediates(): void, 842 | /** 843 | * Executes only the macro task queue (i.e. all tasks queued by setTimeout() 844 | * or setInterval() and setImmediate()). 845 | */ 846 | advanceTimersByTime(msToRun: number): void, 847 | /** 848 | * Executes only the macro task queue (i.e. all tasks queued by setTimeout() 849 | * or setInterval() and setImmediate()). 850 | * 851 | * Renamed to `advanceTimersByTime`. 852 | */ 853 | runTimersToTime(msToRun: number): void, 854 | /** 855 | * Executes only the macro-tasks that are currently pending (i.e., only the 856 | * tasks that have been queued by setTimeout() or setInterval() up to this 857 | * point) 858 | */ 859 | runOnlyPendingTimers(): void, 860 | /** 861 | * Explicitly supplies the mock object that the module system should return 862 | * for the specified module. Note: It is recommended to use jest.mock() 863 | * instead. 864 | */ 865 | setMock(moduleName: string, moduleExports: any): JestObjectType, 866 | /** 867 | * Indicates that the module system should never return a mocked version of 868 | * the specified module from require() (e.g. that it should always return the 869 | * real module). 870 | */ 871 | unmock(moduleName: string): JestObjectType, 872 | /** 873 | * Instructs Jest to use fake versions of the standard timer functions 874 | * (setTimeout, setInterval, clearTimeout, clearInterval, nextTick, 875 | * setImmediate and clearImmediate). 876 | */ 877 | useFakeTimers(): JestObjectType, 878 | /** 879 | * Instructs Jest to use the real versions of the standard timer functions. 880 | */ 881 | useRealTimers(): JestObjectType, 882 | /** 883 | * Creates a mock function similar to jest.fn but also tracks calls to 884 | * object[methodName]. 885 | */ 886 | spyOn(object: Object, methodName: string, accessType?: "get" | "set"): JestMockFn, 887 | /** 888 | * Set the default timeout interval for tests and before/after hooks in milliseconds. 889 | * Note: The default timeout interval is 5 seconds if this method is not called. 890 | */ 891 | setTimeout(timeout: number): JestObjectType 892 | }; 893 | 894 | type JestSpyType = { 895 | calls: JestCallsType 896 | }; 897 | 898 | /** Runs this function after every test inside this context */ 899 | declare function afterEach( 900 | fn: (done: () => void) => ?Promise, 901 | timeout?: number 902 | ): void; 903 | /** Runs this function before every test inside this context */ 904 | declare function beforeEach( 905 | fn: (done: () => void) => ?Promise, 906 | timeout?: number 907 | ): void; 908 | /** Runs this function after all tests have finished inside this context */ 909 | declare function afterAll( 910 | fn: (done: () => void) => ?Promise, 911 | timeout?: number 912 | ): void; 913 | /** Runs this function before any tests have started inside this context */ 914 | declare function beforeAll( 915 | fn: (done: () => void) => ?Promise, 916 | timeout?: number 917 | ): void; 918 | 919 | /** A context for grouping tests together */ 920 | declare var describe: { 921 | /** 922 | * Creates a block that groups together several related tests in one "test suite" 923 | */ 924 | (name: JestTestName, fn: () => void): void, 925 | 926 | /** 927 | * Only run this describe block 928 | */ 929 | only(name: JestTestName, fn: () => void): void, 930 | 931 | /** 932 | * Skip running this describe block 933 | */ 934 | skip(name: JestTestName, fn: () => void): void, 935 | 936 | /** 937 | * each runs this test against array of argument arrays per each run 938 | * 939 | * @param {table} table of Test 940 | */ 941 | each( 942 | table: Array | mixed> 943 | ): ( 944 | name: JestTestName, 945 | fn?: (...args: Array) => ?Promise 946 | ) => void, 947 | }; 948 | 949 | /** An individual test unit */ 950 | declare var it: { 951 | /** 952 | * An individual test unit 953 | * 954 | * @param {JestTestName} Name of Test 955 | * @param {Function} Test 956 | * @param {number} Timeout for the test, in milliseconds. 957 | */ 958 | ( 959 | name: JestTestName, 960 | fn?: (done: () => void) => ?Promise, 961 | timeout?: number 962 | ): void, 963 | /** 964 | * each runs this test against array of argument arrays per each run 965 | * 966 | * @param {table} table of Test 967 | */ 968 | each( 969 | table: Array | mixed> 970 | ): ( 971 | name: JestTestName, 972 | fn?: (...args: Array) => ?Promise 973 | ) => void, 974 | /** 975 | * Only run this test 976 | * 977 | * @param {JestTestName} Name of Test 978 | * @param {Function} Test 979 | * @param {number} Timeout for the test, in milliseconds. 980 | */ 981 | only( 982 | name: JestTestName, 983 | fn?: (done: () => void) => ?Promise, 984 | timeout?: number 985 | ): { 986 | each( 987 | table: Array | mixed> 988 | ): ( 989 | name: JestTestName, 990 | fn?: (...args: Array) => ?Promise 991 | ) => void, 992 | }, 993 | /** 994 | * Skip running this test 995 | * 996 | * @param {JestTestName} Name of Test 997 | * @param {Function} Test 998 | * @param {number} Timeout for the test, in milliseconds. 999 | */ 1000 | skip( 1001 | name: JestTestName, 1002 | fn?: (done: () => void) => ?Promise, 1003 | timeout?: number 1004 | ): void, 1005 | /** 1006 | * Run the test concurrently 1007 | * 1008 | * @param {JestTestName} Name of Test 1009 | * @param {Function} Test 1010 | * @param {number} Timeout for the test, in milliseconds. 1011 | */ 1012 | concurrent( 1013 | name: JestTestName, 1014 | fn?: (done: () => void) => ?Promise, 1015 | timeout?: number 1016 | ): void, 1017 | /** 1018 | * each runs this test against array of argument arrays per each run 1019 | * 1020 | * @param {table} table of Test 1021 | */ 1022 | each( 1023 | table: Array | mixed> 1024 | ): ( 1025 | name: JestTestName, 1026 | fn?: (...args: Array) => ?Promise 1027 | ) => void, 1028 | }; 1029 | declare function fit( 1030 | name: JestTestName, 1031 | fn: (done: () => void) => ?Promise, 1032 | timeout?: number 1033 | ): void; 1034 | /** An individual test unit */ 1035 | declare var test: typeof it; 1036 | /** A disabled group of tests */ 1037 | declare var xdescribe: typeof describe; 1038 | /** A focused group of tests */ 1039 | declare var fdescribe: typeof describe; 1040 | /** A disabled individual test */ 1041 | declare var xit: typeof it; 1042 | /** A disabled individual test */ 1043 | declare var xtest: typeof it; 1044 | 1045 | type JestPrettyFormatColors = { 1046 | comment: { close: string, open: string }, 1047 | content: { close: string, open: string }, 1048 | prop: { close: string, open: string }, 1049 | tag: { close: string, open: string }, 1050 | value: { close: string, open: string }, 1051 | }; 1052 | 1053 | type JestPrettyFormatIndent = string => string; 1054 | type JestPrettyFormatRefs = Array; 1055 | type JestPrettyFormatPrint = any => string; 1056 | type JestPrettyFormatStringOrNull = string | null; 1057 | 1058 | type JestPrettyFormatOptions = {| 1059 | callToJSON: boolean, 1060 | edgeSpacing: string, 1061 | escapeRegex: boolean, 1062 | highlight: boolean, 1063 | indent: number, 1064 | maxDepth: number, 1065 | min: boolean, 1066 | plugins: JestPrettyFormatPlugins, 1067 | printFunctionName: boolean, 1068 | spacing: string, 1069 | theme: {| 1070 | comment: string, 1071 | content: string, 1072 | prop: string, 1073 | tag: string, 1074 | value: string, 1075 | |}, 1076 | |}; 1077 | 1078 | type JestPrettyFormatPlugin = { 1079 | print: ( 1080 | val: any, 1081 | serialize: JestPrettyFormatPrint, 1082 | indent: JestPrettyFormatIndent, 1083 | opts: JestPrettyFormatOptions, 1084 | colors: JestPrettyFormatColors, 1085 | ) => string, 1086 | test: any => boolean, 1087 | }; 1088 | 1089 | type JestPrettyFormatPlugins = Array; 1090 | 1091 | /** The expect function is used every time you want to test a value */ 1092 | declare var expect: { 1093 | /** The object that you want to make assertions against */ 1094 | (value: any): 1095 | & JestExpectType 1096 | & JestPromiseType 1097 | & EnzymeMatchersType 1098 | & DomTestingLibraryType 1099 | & JestJQueryMatchersType 1100 | & JestStyledComponentsMatchersType 1101 | & JestExtendedMatchersType, 1102 | 1103 | /** Add additional Jasmine matchers to Jest's roster */ 1104 | extend(matchers: { [name: string]: JestMatcher }): void, 1105 | /** Add a module that formats application-specific data structures. */ 1106 | addSnapshotSerializer(pluginModule: JestPrettyFormatPlugin): void, 1107 | assertions(expectedAssertions: number): void, 1108 | hasAssertions(): void, 1109 | any(value: mixed): JestAsymmetricEqualityType, 1110 | anything(): any, 1111 | arrayContaining(value: Array): Array, 1112 | objectContaining(value: Object): Object, 1113 | /** Matches any received string that contains the exact expected string. */ 1114 | stringContaining(value: string): string, 1115 | stringMatching(value: string | RegExp): string, 1116 | not: { 1117 | arrayContaining: (value: $ReadOnlyArray) => Array, 1118 | objectContaining: (value: {}) => Object, 1119 | stringContaining: (value: string) => string, 1120 | stringMatching: (value: string | RegExp) => string, 1121 | }, 1122 | }; 1123 | 1124 | // TODO handle return type 1125 | // http://jasmine.github.io/2.4/introduction.html#section-Spies 1126 | declare function spyOn(value: mixed, method: string): Object; 1127 | 1128 | /** Holds all functions related to manipulating test runner */ 1129 | declare var jest: JestObjectType; 1130 | 1131 | /** 1132 | * The global Jasmine object, this is generally not exposed as the public API, 1133 | * using features inside here could break in later versions of Jest. 1134 | */ 1135 | declare var jasmine: { 1136 | DEFAULT_TIMEOUT_INTERVAL: number, 1137 | any(value: mixed): JestAsymmetricEqualityType, 1138 | anything(): any, 1139 | arrayContaining(value: Array): Array, 1140 | clock(): JestClockType, 1141 | createSpy(name: string): JestSpyType, 1142 | createSpyObj( 1143 | baseName: string, 1144 | methodNames: Array 1145 | ): { [methodName: string]: JestSpyType }, 1146 | objectContaining(value: Object): Object, 1147 | stringMatching(value: string): string 1148 | }; 1149 | --------------------------------------------------------------------------------