├── .all-contributorsrc ├── .babelrc ├── .eslintignore ├── .gitattributes ├── .gitignore ├── .npmrc ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── app ├── .babelrc ├── .gitignore ├── README.md ├── app.js ├── example-code │ ├── .eslintrc │ ├── usage.js │ └── utils.js ├── index.html ├── index.js ├── package.json ├── slicer.js └── yarn.lock ├── integration ├── .babelrc ├── __tests__ │ ├── __sliceshots__ │ │ └── redux │ │ │ └── lib │ │ │ ├── applyMiddleware.js.slice │ │ │ ├── bindActionCreators.js.slice │ │ │ ├── combineReducers.js.slice │ │ │ ├── compose.js.slice │ │ │ ├── createStore.js.slice │ │ │ ├── index.js.slice │ │ │ └── utils │ │ │ └── warning.js.slice │ └── redux.js ├── n_modules ├── package.json ├── scripts │ └── process-coverage.js └── yarn.lock ├── other ├── CODE_OF_CONDUCT.md ├── EXAMPLES.md ├── ROADMAP.md ├── learning-code.md └── ultra-tree-shaking.md ├── package-scripts.js ├── package.json ├── src ├── __snapshots__ │ └── index.test.js.snap ├── index.js ├── index.test.js └── slice-code │ ├── babel-plugin-custom-dead-code-elimination.js │ ├── get-sliced-code-transform.js │ ├── index.js │ ├── test │ ├── __snapshots__ │ │ ├── assignment-expression.test.js.snap │ │ ├── assignment-ternary.test.js.snap │ │ ├── async-code.test.js.snap │ │ ├── clone.test.js.snap │ │ ├── complex-fns.test.js.snap │ │ ├── cond-expr.test.js.snap │ │ ├── early-exit.test.js.snap │ │ ├── functions.test.js.snap │ │ ├── if-statement-side-effects.test.js.snap │ │ ├── if-statements.test.js.snap │ │ ├── indent-string.test.js.snap │ │ ├── logical-expressions.test.js.snap │ │ ├── match-sorter.test.js.snap │ │ ├── module-pattern.test.js.snap │ │ ├── pizza.test.js.snap │ │ ├── switch-statement.test.js.snap │ │ ├── try-catch.test.js.snap │ │ └── unused-assignment.test.js.snap │ ├── assignment-expression.test.js │ ├── assignment-ternary.test.js │ ├── async-code.test.js │ ├── clone.test.js │ ├── complex-fns.test.js │ ├── cond-expr.test.js │ ├── early-exit.test.js │ ├── fixtures │ │ ├── assignment-expressions.js │ │ ├── assignment-ternary.js │ │ ├── async-code.js │ │ ├── clone.js │ │ ├── complex-fns.js │ │ ├── cond-expr.js │ │ ├── early-exit.js │ │ ├── functions.js │ │ ├── if-statement-side-effects.js │ │ ├── if-statements.js │ │ ├── logical-expressions.js │ │ ├── module-pattern.js │ │ ├── pizza.js │ │ ├── switch-statement.js │ │ ├── try-catch.js │ │ └── unused-assignment.js │ ├── functions.test.js │ ├── helpers │ │ └── utils.js │ ├── if-statement-side-effects.test.js │ ├── if-statements.test.js │ ├── indent-string.test.js │ ├── logical-expressions.test.js │ ├── match-sorter.test.js │ ├── module-pattern.test.js │ ├── pizza.test.js │ ├── switch-statement.test.js │ ├── try-catch.test.js │ └── unused-assignment.test.js │ └── transform-coverage.js ├── test └── fixtures │ ├── .babelrc │ ├── module.js │ └── module.slicer.js └── yarn.lock /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "slice-js", 3 | "projectOwner": "kentcdodds", 4 | "files": [ 5 | "README.md" 6 | ], 7 | "imageSize": 100, 8 | "commit": false, 9 | "contributors": [ 10 | { 11 | "login": "kentcdodds", 12 | "name": "Kent C. Dodds", 13 | "avatar_url": "https://avatars.githubusercontent.com/u/1500684?v=3", 14 | "profile": "https://kentcdodds.com", 15 | "contributions": [ 16 | "code", 17 | "test" 18 | ] 19 | } 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["node6", "stage-2"] 3 | } 4 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .nyc_output 3 | coverage 4 | dist 5 | *.ignored.* 6 | fixtures 7 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.js text eol=lf 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .nyc_output 3 | coverage 4 | dist 5 | .opt-in 6 | .opt-out 7 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=http://registry.npmjs.org/ 2 | save-exact=true 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | cache: 4 | directories: 5 | - node_modules 6 | notifications: 7 | email: false 8 | node_js: 9 | - '6' 10 | before_script: 11 | - npm prune 12 | script: 13 | - npm start validate 14 | branches: 15 | only: 16 | - master 17 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | The changelog is automatically updated using [semantic-release](https://github.com/semantic-release/semantic-release). 4 | You can see it on the [releases page](../releases). 5 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Thanks for being willing to contribute! 4 | 5 | **Working on your first Pull Request?** You can learn how from this *free* series 6 | [How to Contribute to an Open Source Project on GitHub][egghead] 7 | 8 | ## Project setup 9 | 10 | 1. Fork and clone the repo 11 | 2. `$ npm install` to install dependencies 12 | 3. `$ npm start validate` to validate you've got it working 13 | 4. Create a branch for your PR 14 | 15 | This project uses [`nps`][nps] and you can run `npm start` to see what scripts are available. 16 | 17 | ## Add yourself as a contributor 18 | 19 | This project follows the [all contributors][all-contributors] specification. To add yourself to the table of 20 | contributors on the README.md, please use the automated script as part of your PR: 21 | 22 | ```console 23 | npm start "addContributor " 24 | ``` 25 | 26 | Follow the prompt. If you've already added yourself to the list and are making a new type of contribution, you can run 27 | it again and select the added contribution type. 28 | 29 | ## Committing and Pushing changes 30 | 31 | This project uses [`semantic-release`][semantic-release] to do automatic releases and generate a changelog based on the 32 | commit history. So we follow [a convention][convention] for commit messages. Please follow this convention for your 33 | commit messages. 34 | 35 | You can use `commitizen` to help you to follow [the convention][convention] 36 | 37 | Once you are ready to commit the changes, please use the below commands 38 | 39 | 1. `git add ` 40 | 2. `$ npm start commit` 41 | 42 | ... and follow the instruction of the interactive prompt. 43 | 44 | ### opt into git hooks 45 | 46 | There are git hooks set up with this project that are automatically installed when you install dependencies. They're 47 | really handy, but are turned off by default (so as to not hinder new contributors). You can opt into these by creating 48 | a file called `.opt-in` at the root of the project and putting this inside: 49 | 50 | ``` 51 | commit-msg 52 | pre-commit 53 | ``` 54 | 55 | ## Help needed 56 | 57 | Please checkout the [ROADMAP.md][ROADMAP] and raise an issue to discuss 58 | any of the items in the want to do or might do list. 59 | 60 | Also, please watch the repo and respond to questions/bug reports/feature requests! Thanks! 61 | 62 | [egghead]: https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github 63 | [semantic-release]: https://npmjs.com/package/semantic-release 64 | [convention]: https://github.com/conventional-changelog/conventional-changelog-angular/blob/ed32559941719a130bb0327f886d6a32a8cbc2ba/convention.md 65 | [all-contributors]: https://github.com/kentcdodds/all-contributors 66 | [ROADMAP]: ./ROADMAP.md 67 | [nps]: https://npmjs.com/package/nps 68 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Kent C. Dodds 2 | 3 | All Rights Reserved. 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SliceJS 2 | 3 | A tool to generate a program slice of your JavaScript code 4 | 5 | **It's definitely a work in progress!** 6 | 7 | [![Build Status][build-badge]][build] 8 | [![Code Coverage][coverage-badge]][coverage] 9 | [![Dependencies][dependencyci-badge]][dependencyci] 10 | [![version][version-badge]][package] 11 | [![downloads][downloads-badge]][npm-stat] 12 | [![MIT License][license-badge]][LICENSE] 13 | 14 | [![All Contributors](https://img.shields.io/badge/all_contributors-1-orange.svg?style=flat-square)](#contributors) 15 | [![PRs Welcome][prs-badge]][prs] 16 | [![Donate][donate-badge]][donate] 17 | [![Code of Conduct][coc-badge]][coc] 18 | [![Roadmap][roadmap-badge]][roadmap] 19 | [![Examples][examples-badge]][examples] 20 | 21 | [![Watch on GitHub][github-watch-badge]][github-watch] 22 | [![Star on GitHub][github-star-badge]][github-star] 23 | [![Tweet][twitter-badge]][twitter] 24 | 25 | ## The problem 26 | 27 | Familiarizing yourself with a codebase is hard. Learn more about program slicing [here][slicing-wikipedia] 28 | 29 | ## This solution 30 | 31 | This will generate a program slice for the given files and tests. 32 | 33 | ## Installation 34 | 35 | This module is distributed via [npm][npm] which is bundled with [node][node] and should 36 | be installed as one of your project's `devDependencies`: 37 | 38 | ``` 39 | npm install --save-dev slice-js 40 | ``` 41 | 42 | ## Usage 43 | 44 | This is still being worked on... See the test/fixtures... 45 | 46 | ## Inspiration 47 | 48 | A talk by [@inconshreveable](https://github.com/inconshreveable) at The Strange Loop 2016 called 49 | ["Idealized Commit Logs: Code Simplification via Program Slicing"](https://youtu.be/dSqLt8BgbRQ) 50 | about the amazing tools that can be built with program slicing. 51 | 52 | ## Other Solutions 53 | 54 | The only other solution I'm aware of is not open source AFAIK: [JSSlicer](http://www.scientific.net/AMM.241-244.2690) 55 | 56 | ## Contributors 57 | 58 | 59 | | [
Kent C. Dodds](https://kentcdodds.com)
[💻](https://github.com/kentcdodds/slice-js/commits?author=kentcdodds) [⚠️](https://github.com/kentcdodds/slice-js/commits?author=kentcdodds) | 60 | | :---: | 61 | 62 | Thanks goes to these people ([emoji key][emojis]): 63 | 64 | 65 | 66 | 67 | This project follows the [all-contributors][all-contributors] specification. Contributions of any kind welcome! 68 | 69 | ## LICENSE 70 | 71 | MIT 72 | 73 | [npm]: https://www.npmjs.com/ 74 | [node]: https://nodejs.org 75 | [build-badge]: https://img.shields.io/travis/kentcdodds/slice-js.svg?style=flat-square 76 | [build]: https://travis-ci.org/kentcdodds/slice-js 77 | [coverage-badge]: https://img.shields.io/codecov/c/github/kentcdodds/slice-js.svg?style=flat-square 78 | [coverage]: https://codecov.io/github/kentcdodds/slice-js 79 | [dependencyci-badge]: https://dependencyci.com/github/kentcdodds/slice-js/badge?style=flat-square 80 | [dependencyci]: https://dependencyci.com/github/kentcdodds/slice-js 81 | [version-badge]: https://img.shields.io/npm/v/slice-js.svg?style=flat-square 82 | [package]: https://www.npmjs.com/package/slice-js 83 | [downloads-badge]: https://img.shields.io/npm/dm/slice-js.svg?style=flat-square 84 | [npm-stat]: http://npm-stat.com/charts.html?package=slice-js&from=2016-04-01 85 | [license-badge]: https://img.shields.io/npm/l/slice-js.svg?style=flat-square 86 | [license]: https://github.com/kentcdodds/slice-js/blob/master/other/LICENSE 87 | [prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square 88 | [prs]: http://makeapullrequest.com 89 | [donate-badge]: https://img.shields.io/badge/$-support-green.svg?style=flat-square 90 | [donate]: http://kcd.im/donate 91 | [coc-badge]: https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square 92 | [coc]: https://github.com/kentcdodds/slice-js/blob/master/other/CODE_OF_CONDUCT.md 93 | [roadmap-badge]: https://img.shields.io/badge/%F0%9F%93%94-roadmap-CD9523.svg?style=flat-square 94 | [roadmap]: https://github.com/kentcdodds/slice-js/blob/master/other/ROADMAP.md 95 | [examples-badge]: https://img.shields.io/badge/%F0%9F%92%A1-examples-8C8E93.svg?style=flat-square 96 | [examples]: https://github.com/kentcdodds/slice-js/blob/master/other/EXAMPLES.md 97 | [github-watch-badge]: https://img.shields.io/github/watchers/kentcdodds/slice-js.svg?style=social 98 | [github-watch]: https://github.com/kentcdodds/slice-js/watchers 99 | [github-star-badge]: https://img.shields.io/github/stars/kentcdodds/slice-js.svg?style=social 100 | [github-star]: https://github.com/kentcdodds/slice-js/stargazers 101 | [twitter]: https://twitter.com/intent/tweet?text=Check%20out%20slice-js!%20https://github.com/kentcdodds/slice-js%20%F0%9F%91%8D 102 | [twitter-badge]: https://img.shields.io/twitter/url/https/github.com/kentcdodds/slice-js.svg?style=social 103 | [emojis]: https://github.com/kentcdodds/all-contributors#emoji-key 104 | [all-contributors]: https://github.com/kentcdodds/all-contributors 105 | [slicing-wikipedia]: https://en.wikipedia.org/wiki/Program_slicing 106 | -------------------------------------------------------------------------------- /app/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["node6", "stage-2", "react"] 3 | } 4 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /app/README.md: -------------------------------------------------------------------------------- 1 | # slice-js app 2 | 3 | This is an electron app to make checking code faster. 4 | -------------------------------------------------------------------------------- /app/app.js: -------------------------------------------------------------------------------- 1 | // Assuming this file is ./src/es6-init.js 2 | const appRoot = __dirname 3 | 4 | // ...and that your main app is called ./src/main.js. This is written as if 5 | // you were going to `require` the file from here. 6 | require('electron-compile').init(appRoot, './slicer') 7 | -------------------------------------------------------------------------------- /app/example-code/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "parserOptions": { 4 | "ecmaVersion": 7, 5 | "sourceType": "module" 6 | }, 7 | "rules": { 8 | "semi": [2, "never"] 9 | } 10 | } -------------------------------------------------------------------------------- /app/example-code/usage.js: -------------------------------------------------------------------------------- 1 | function useUtils({clone}) { 2 | return [clone()] 3 | } 4 | -------------------------------------------------------------------------------- /app/example-code/utils.js: -------------------------------------------------------------------------------- 1 | export {deepFreeze, clone} 2 | 3 | function deepFreeze(o) { 4 | Object.freeze(o) 5 | 6 | Object.getOwnPropertyNames(o).forEach(prop => { 7 | if ( 8 | o.hasOwnProperty(prop) && 9 | o[prop] !== null && 10 | (typeof o[prop] === 'object' || typeof o[prop] === 'function') && 11 | !Object.isFrozen(o[prop]) 12 | ) { 13 | deepFreeze(o[prop]) 14 | } 15 | }) 16 | 17 | return o 18 | } 19 | 20 | function clone(item) { 21 | /* eslint complexity:[2, 11] max-depth:[2, 6] */ 22 | if (!item) { 23 | return item 24 | } 25 | const type = typeof item 26 | const string = Object.prototype.toString.call(item) 27 | const isPrimitive = type !== 'object' && type !== 'function' 28 | let result = item 29 | 30 | if (!isPrimitive) { 31 | if (string === '[object Array]') { 32 | result = [] 33 | item.forEach((child, index) => { 34 | result[index] = clone(child) 35 | }) 36 | } else if (type === 'object') { 37 | if (item.nodeType && typeof item.cloneNode === 'function') { 38 | result = item.cloneNode(true) 39 | } else if (!item.prototype) { 40 | if (string === '[object Date]') { 41 | result = new Date(item) 42 | } else { 43 | result = {} 44 | for (const i in item) { 45 | if (item.hasOwnProperty(i)) { 46 | result[i] = clone(item[i]) 47 | } 48 | } 49 | } 50 | } else if (item.constructor) { 51 | result = new item.constructor() 52 | } else { 53 | result = item 54 | } 55 | } 56 | } 57 | 58 | return result 59 | } 60 | -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SliceJS 6 | 7 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/index.js: -------------------------------------------------------------------------------- 1 | // copied from 2 | // https://github.com/electron/electron/blob/master/docs/tutorial/quick-start.md 3 | const {app, BrowserWindow} = require('electron') 4 | 5 | // Keep a global reference of the window object, if you don't, the window will 6 | // be closed automatically when the JavaScript object is garbage collected. 7 | let win 8 | 9 | function createWindow() { 10 | // Create the browser window. 11 | win = new BrowserWindow({width: 800, height: 600}) 12 | 13 | // and load the index.html of the app. 14 | win.loadURL(`file://${__dirname}/index.html`) 15 | 16 | // Open the DevTools. 17 | win.webContents.openDevTools() 18 | 19 | // Emitted when the window is closed. 20 | win.on('closed', () => { 21 | // Dereference the window object, usually you would store windows 22 | // in an array if your app supports multi windows, this is the time 23 | // when you should delete the corresponding element. 24 | win = null 25 | }) 26 | } 27 | 28 | // This method will be called when Electron has finished 29 | // initialization and is ready to create browser windows. 30 | // Some APIs can only be used after this event occurs. 31 | app.on('ready', createWindow) 32 | 33 | // Quit when all windows are closed. 34 | app.on('window-all-closed', () => { 35 | // On macOS it is common for applications and their menu bar 36 | // to stay active until the user quits explicitly with Cmd + Q 37 | if (process.platform !== 'darwin') { 38 | app.quit() 39 | } 40 | }) 41 | 42 | app.on('activate', () => { 43 | // On macOS it's common to re-create a window in the app when the 44 | // dock icon is clicked and there are no other windows open. 45 | if (win === null) { 46 | createWindow() 47 | } 48 | }) 49 | 50 | // In this file you can include the rest of your app's specific main process 51 | // code. You can also put them in separate files and require them here. 52 | // 53 | -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "slice-js-app", 3 | "version": "1.0.0", 4 | "description": "This is an electron app to make checking code faster.", 5 | "main": "index.js", 6 | "scripts": { 7 | "start": "electron ." 8 | }, 9 | "keywords": [], 10 | "author": "Kent C. Dodds (http://kentcdodds.com/)", 11 | "license": "MIT", 12 | "dependencies": { 13 | "aphrodite": "^1.2.0", 14 | "codemirror": "^5.21.0", 15 | "copy-paste": "^1.3.0", 16 | "electron": "^1.6.2", 17 | "electron-compile": "^6.1.3", 18 | "jest": "^17.0.3", 19 | "lodash": "^4.17.4", 20 | "open": "^0.0.5", 21 | "react": "^15.4.2", 22 | "react-codemirror": "^0.3.0", 23 | "react-dom": "^15.4.2" 24 | }, 25 | "devDependencies": { 26 | "babel-core": "^6.24.0", 27 | "babel-preset-node6": "^11.0.0", 28 | "babel-preset-react": "^6.23.0", 29 | "babel-preset-stage-2": "^6.22.0", 30 | "babel-register": "^6.24.0", 31 | "electron-compilers": "^5.5.1", 32 | "eslint": "^3.17.1", 33 | "eslint-config-kentcdodds": "^12.2.0", 34 | "eslint-plugin-babel": "^4.1.1", 35 | "eslint-plugin-react": "^6.10.0" 36 | }, 37 | "eslintConfig": { 38 | "extends": [ 39 | "kentcdodds", 40 | "kentcdodds/react", 41 | "kentcdodds/prettier" 42 | ] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /app/slicer.js: -------------------------------------------------------------------------------- 1 | /* eslint no-invalid-this:0 */ 2 | import fs from 'fs' 3 | import {isEqual} from 'lodash' 4 | import {copy} from 'copy-paste' 5 | import open from 'open' 6 | import React, {Component, PropTypes} from 'react' 7 | import ReactDOM from 'react-dom' 8 | import Codemirror from 'react-codemirror' 9 | // eslint-disable-next-line import/no-unassigned-import 10 | import 'codemirror/mode/javascript/javascript' 11 | 12 | import {getSliceAndInfo} from '../src/slice-code/test/helpers/utils' 13 | 14 | const sampleCode = fs.readFileSync( 15 | require.resolve('./example-code/utils'), 16 | 'utf-8', 17 | ) 18 | const usageSample = fs.readFileSync( 19 | require.resolve('./example-code/usage'), 20 | 'utf-8', 21 | ) 22 | 23 | class App extends Component { 24 | state = { 25 | slicedCode: '', 26 | moduleUsage: usageSample, 27 | codeToSlice: sampleCode, 28 | autoRun: true, 29 | } 30 | 31 | componentDidMount() { 32 | this.updateSlice() 33 | } 34 | 35 | updateSlice = async force => { 36 | const {codeToSlice, moduleUsage, autoRun} = this.state 37 | if (!autoRun && !force) { 38 | return 39 | } 40 | const usageFn = getUsageFunction(moduleUsage) 41 | if (!usageFn) { 42 | return 43 | } 44 | let success = false 45 | const sliceInfo = await getSliceAndInfo(codeToSlice, async mod => { 46 | try { 47 | const ret = await usageFn(mod) 48 | success = true 49 | return ret 50 | } catch (e) { 51 | return undefined 52 | } 53 | }) 54 | if (!success) { 55 | return 56 | } 57 | const { 58 | originalResult, 59 | slicedCode, 60 | slicedResult, 61 | isSlicedCoverage100, 62 | filteredCoverage, 63 | } = sliceInfo 64 | this.setState({ 65 | slicedCode, 66 | originalResult, 67 | slicedResult, 68 | isSlicedCoverage100, 69 | filteredCoverage, 70 | }) 71 | } 72 | 73 | onCodeSliceChange = value => { 74 | this.setState( 75 | { 76 | codeToSlice: value, 77 | slicedCode: '', 78 | originalResult: undefined, 79 | slicedResult: undefined, 80 | isSlicedCoverage100: true, 81 | filteredCoverage: null, 82 | }, 83 | this.updateSlice, 84 | ) 85 | } 86 | 87 | onModuleUsageChange = value => { 88 | this.setState( 89 | { 90 | moduleUsage: value, 91 | }, 92 | this.updateSlice, 93 | ) 94 | } 95 | 96 | render() { 97 | const { 98 | originalResult, 99 | slicedCode, 100 | slicedResult, 101 | isSlicedCoverage100, 102 | codeToSlice, 103 | filteredCoverage, 104 | moduleUsage, 105 | autoRun, 106 | } = this.state 107 | const sameResult = isEqual(originalResult, slicedResult) 108 | const codemirrorOptions = { 109 | lineNumbers: true, 110 | mode: 'javascript', 111 | } 112 | const slicedStyles = { 113 | marginTop: 30, 114 | border: isSlicedCoverage100 && sameResult ? '' : 'solid 1px red', 115 | } 116 | return ( 117 |
118 |

🍕 SliceJS 🍕

119 |
120 |
121 | Code to slice 122 |
123 | 128 |
129 |
130 | Usage 131 |
132 | 137 |
138 |
139 |
140 | Slice 141 |
142 | 151 |
152 | 157 | 158 | {isSlicedCoverage100 ? 159 |
160 | Sliced coverage is 100%, we couldn't slice more... 161 | Although there may be optimizations we could 162 | make with regards to data allocation... 163 |
: 164 |
165 | Sliced coverage is not 100%, we could slice more... 166 |
} 167 | {sameResult ? 168 |
169 | The returned from both the original module and 170 | the sliced version is the same. 171 |
172 | Result: 173 |
{JSON.stringify(originalResult, null, 2)}
174 |
: 175 |
176 | The returned from both the original module 177 | and the sliced version is different! 178 |
179 | Original Result: 180 |
{JSON.stringify(originalResult, null, 2)}
181 | Slice Result: 182 |
{JSON.stringify(slicedResult, null, 2)}
183 |
} 184 | 185 |
186 |
187 | ) 188 | } 189 | } 190 | 191 | function ASTExplorerCode({filteredCoverage = {}}) { 192 | return ( 193 |
194 | Generate plugin for astexplorer.net. 195 | 196 |
197 | ) 198 | 199 | function handleClick() { 200 | const getSlicedTransformModuleString = fs.readFileSync( 201 | '../src/slice-code/get-sliced-code-transform.js', 202 | 'utf-8', 203 | ) 204 | const textToCopy = ` 205 | const filteredCoverage = ${JSON.stringify(filteredCoverage)} 206 | 207 | ${getSlicedTransformModuleString 208 | .replace( 209 | 'export default getSliceCodeTransform', 210 | 'export default getSliceCodeTransform(filteredCoverage)', 211 | ) 212 | .replace(/\/\* eslint.*\n/, '')} 213 | `.trim() 214 | copy(textToCopy, () => { 215 | const gistId = 'a0bf17981b123436e45713eb162220aa' 216 | const gistCommit = '085fa28e74258e2512dea8229a9c3d06778fb399' 217 | open(`https://astexplorer.net/#/gist/${gistId}/${gistCommit}`) 218 | }) 219 | } 220 | } 221 | 222 | ASTExplorerCode.propTypes = { 223 | filteredCoverage: PropTypes.object, 224 | } 225 | 226 | const root = document.getElementById('root') 227 | ReactDOM.render(, root) 228 | 229 | function getUsageFunction(code) { 230 | /* eslint no-new-func:0 */ 231 | try { 232 | return new Function(`return (${code.trim()})`)() 233 | } catch (error) { 234 | return undefined 235 | } 236 | } 237 | -------------------------------------------------------------------------------- /integration/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["node6", "stage-2"] 3 | } 4 | -------------------------------------------------------------------------------- /integration/__tests__/__sliceshots__/redux/lib/applyMiddleware.js.slice: -------------------------------------------------------------------------------- 1 | exports[`/Users/kdodds/Developer/slice-js/integration/node_modules/redux/lib/applyMiddleware.js`] = ` 2 | "\'use strict\'; 3 | 4 | exports.__esModule = true; 5 | 6 | var _compose = require(\'./compose\'); 7 | 8 | function _interopRequireDefault(obj) { 9 | return obj; 10 | }" 11 | `; 12 | -------------------------------------------------------------------------------- /integration/__tests__/__sliceshots__/redux/lib/bindActionCreators.js.slice: -------------------------------------------------------------------------------- 1 | exports[`/Users/kdodds/Developer/slice-js/integration/node_modules/redux/lib/bindActionCreators.js`] = ` 2 | "\'use strict\'; 3 | 4 | exports.__esModule = true;" 5 | `; 6 | -------------------------------------------------------------------------------- /integration/__tests__/__sliceshots__/redux/lib/combineReducers.js.slice: -------------------------------------------------------------------------------- 1 | exports[`/Users/kdodds/Developer/slice-js/integration/node_modules/redux/lib/combineReducers.js`] = ` 2 | "\'use strict\'; 3 | 4 | exports.__esModule = true; 5 | 6 | var _isPlainObject = require(\'lodash/isPlainObject\'); 7 | 8 | var _warning = require(\'./utils/warning\'); 9 | 10 | function _interopRequireDefault(obj) { 11 | return obj && obj.__esModule ? obj : { \'default\': obj }; 12 | }" 13 | `; 14 | -------------------------------------------------------------------------------- /integration/__tests__/__sliceshots__/redux/lib/compose.js.slice: -------------------------------------------------------------------------------- 1 | exports[`/Users/kdodds/Developer/slice-js/integration/node_modules/redux/lib/compose.js`] = ` 2 | "\"use strict\"; 3 | 4 | exports.__esModule = true;" 5 | `; 6 | -------------------------------------------------------------------------------- /integration/__tests__/__sliceshots__/redux/lib/createStore.js.slice: -------------------------------------------------------------------------------- 1 | exports[`/Users/kdodds/Developer/slice-js/integration/node_modules/redux/lib/createStore.js`] = ` 2 | "\'use strict\'; 3 | 4 | exports.__esModule = true; 5 | exports.ActionTypes = undefined; 6 | exports[\'default\'] = createStore; 7 | 8 | var _isPlainObject = require(\'lodash/isPlainObject\'); 9 | 10 | var _symbolObservable = require(\'symbol-observable\'); 11 | 12 | function _interopRequireDefault(obj) { 13 | return obj && obj.__esModule ? obj : { \'default\': obj }; 14 | } 15 | 16 | var ActionTypes = exports.ActionTypes = { 17 | INIT: \'@@redux/INIT\' 18 | }; 19 | 20 | function createStore(reducer, preloadedState) { 21 | var currentState = preloadedState; 22 | var currentListeners = []; 23 | var nextListeners = currentListeners; 24 | 25 | (function (action) { 26 | 27 | try { 28 | currentState = reducer(currentState, action); 29 | } finally {} 30 | 31 | var listeners = currentListeners = nextListeners; 32 | for (var i = 0; i < listeners.length; i++) { 33 | listeners[i](); 34 | } 35 | 36 | return action; 37 | })({ type: ActionTypes.INIT }); 38 | }" 39 | `; 40 | -------------------------------------------------------------------------------- /integration/__tests__/__sliceshots__/redux/lib/index.js.slice: -------------------------------------------------------------------------------- 1 | exports[`/Users/kdodds/Developer/slice-js/integration/node_modules/redux/lib/index.js`] = ` 2 | "\'use strict\'; 3 | 4 | exports.__esModule = true; 5 | exports.compose = exports.applyMiddleware = exports.bindActionCreators = exports.combineReducers = exports.createStore = undefined; 6 | 7 | var _createStore = require(\'./createStore\'); 8 | 9 | var _createStore2 = _interopRequireDefault(_createStore); 10 | 11 | var _combineReducers = require(\'./combineReducers\'); 12 | 13 | var _combineReducers2 = _interopRequireDefault(_combineReducers); 14 | 15 | var _bindActionCreators = require(\'./bindActionCreators\'); 16 | 17 | var _bindActionCreators2 = _interopRequireDefault(_bindActionCreators); 18 | 19 | var _applyMiddleware = require(\'./applyMiddleware\'); 20 | 21 | var _applyMiddleware2 = _interopRequireDefault(_applyMiddleware); 22 | 23 | var _compose = require(\'./compose\'); 24 | 25 | var _compose2 = _interopRequireDefault(_compose); 26 | 27 | var _warning = require(\'./utils/warning\'); 28 | 29 | function _interopRequireDefault(obj) { 30 | return obj; 31 | } 32 | 33 | exports.createStore = _createStore2[\'default\']; 34 | exports.combineReducers = _combineReducers2[\'default\']; 35 | exports.bindActionCreators = _bindActionCreators2[\'default\']; 36 | exports.applyMiddleware = _applyMiddleware2[\'default\']; 37 | exports.compose = _compose2[\'default\'];" 38 | `; 39 | -------------------------------------------------------------------------------- /integration/__tests__/__sliceshots__/redux/lib/utils/warning.js.slice: -------------------------------------------------------------------------------- 1 | exports[`/Users/kdodds/Developer/slice-js/integration/node_modules/redux/lib/utils/warning.js`] = ` 2 | "\'use strict\'; 3 | 4 | exports.__esModule = true;" 5 | `; 6 | -------------------------------------------------------------------------------- /integration/__tests__/redux.js: -------------------------------------------------------------------------------- 1 | import {createStore} from '../n_modules/redux' 2 | 3 | const reducer = (state = 0) => state++ // eslint-disable-line func-style 4 | const store = createStore(reducer) 5 | const unsub = store.subscribe(() => {}) 6 | store.dispatch({type: 'blah'}) 7 | unsub() 8 | 9 | test('temp', () => { 10 | expect(true).toBe(true) 11 | }) 12 | -------------------------------------------------------------------------------- /integration/n_modules: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /integration/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "slice-js-integration", 3 | "version": "1.0.0", 4 | "description": "A place for a bunch of tests for slice-js", 5 | "main": "index.js", 6 | "author": "Kent C. Dodds (http://kentcdodds.com/)", 7 | "license": "MIT", 8 | "scripts": { 9 | "test": "npm run cover && npm run process-coverage", 10 | "cover": "jest --coverage", 11 | "process-coverage": "babel-node ./scripts/process-coverage.js", 12 | "watch:process-coverage": "nodemon --watch ./scripts/ --watch ./__tests__/ --watch ../src --exec \"npm run process-coverage\"" 13 | }, 14 | "dependencies": { 15 | "common-tags": "^1.4.0", 16 | "jest-snapshot": "^17.0.3", 17 | "redux": "^3.6.0" 18 | }, 19 | "devDependencies": { 20 | "babel-cli": "^6.18.0", 21 | "babel-jest": "^17.0.2", 22 | "babel-preset-node6": "^11.0.0", 23 | "babel-preset-stage-2": "^6.18.0", 24 | "jest-cli": "^17.0.3", 25 | "nodemon": "^1.11.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /integration/scripts/process-coverage.js: -------------------------------------------------------------------------------- 1 | /* eslint no-console:0 */ 2 | import fs from 'fs' 3 | import {oneLine} from 'common-tags' 4 | import {SnapshotState} from 'jest-snapshot' 5 | import {sliceCodeAndGetInfo} from '../../src/slice-code' 6 | 7 | const allCoverage = require('../coverage/coverage-final.json') 8 | 9 | Object.keys(allCoverage) 10 | .map(filepath => { 11 | const source = fs.readFileSync(filepath, 'utf8') 12 | const fileCoverage = allCoverage[filepath] 13 | const sliceInfo = sliceCodeAndGetInfo(source, fileCoverage) 14 | if (sliceInfo.error) { 15 | console.info( 16 | `${filepath} failed to slice:\n${sliceInfo.error.message}\n\n`, 17 | ) 18 | console.info( 19 | oneLine(String.raw)` 20 | Here's the filtered coverage: 21 | \n\n${JSON.stringify(sliceInfo.filteredCoverage)}\n\n 22 | `, 23 | ) 24 | console.info(`Here's the source:\n\n${source}\n\n`) 25 | // ignore... 26 | } 27 | return {filepath, source, slice: sliceInfo.slice} 28 | }) 29 | .filter(({slice}) => !!slice) 30 | .forEach(({filepath, slice}) => { 31 | const update = false 32 | const sliceshotsPath = filepath.replace( 33 | 'node_modules', 34 | '__tests__/__sliceshots__', 35 | ) 36 | const snapshotPath = `${sliceshotsPath}.slice` 37 | const expand = false 38 | const state = new SnapshotState(filepath, update, snapshotPath, expand) 39 | state.update = true 40 | const matchResult = state.match(filepath, slice, filepath) 41 | const saveResult = state.save(true) 42 | console.log({matchResult, saveResult}) 43 | }) 44 | -------------------------------------------------------------------------------- /other/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at kent+coc@doddsfamily.us. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /other/EXAMPLES.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | -------------------------------------------------------------------------------- /other/ROADMAP.md: -------------------------------------------------------------------------------- 1 | # Project Roadmap 2 | 3 | ## Want to do 4 | 5 | - too much to mention 6 | 7 | ## Might do 8 | 9 | 10 | 11 | ## Wont do 12 | -------------------------------------------------------------------------------- /other/learning-code.md: -------------------------------------------------------------------------------- 1 | # Learning Code 2 | 3 | One of the original goals to this project is to help developers learn code. Take this module for example: 4 | 5 | ```javascript 6 | export default clone 7 | 8 | function clone(item) { 9 | if (!item) { 10 | return item 11 | } 12 | const type = typeof item 13 | const string = Object.prototype.toString.call(item) 14 | const isPrimitive = type !== "object" && type !== "function" 15 | let result = item 16 | 17 | if (!isPrimitive) { 18 | if (string === '[object Array]') { 19 | result = [] 20 | item.forEach((child, index, array) => { 21 | result[index] = clone(child) 22 | }) 23 | } else if (type === 'object') { 24 | if (item.nodeType && typeof item.cloneNode == 'function') { 25 | result = item.cloneNode(true) 26 | } else if (!item.prototype) { 27 | if (string === '[object Date]') { 28 | result = new Date(item) 29 | } else { 30 | result = {} 31 | for (const i in item) { 32 | result[i] = clone(item[i]) 33 | } 34 | } 35 | } else { 36 | if (false && item.constructor) { 37 | result = new item.constructor() 38 | } else { 39 | result = item 40 | } 41 | } 42 | } 43 | } 44 | 45 | return result 46 | } 47 | ``` 48 | 49 | The `clone` function is `38` lines of code and has a 50 | [cyclomatic complexity](https://en.wikipedia.org/wiki/Cyclomatic_complexity) of `10`. Not exactly the most simple code in 51 | the world! All the branches that handle edge cases make learning how this works at least a 10 minute task. But with 52 | slice-js, you can learn it much more quickly! Let's use `slice-js` to learn this code. 53 | 54 | `slice-js` takes two inputs: The source code, and a code coverage report. Based on this information, it can create a 55 | slice of the program that's relevant for that coverage. Let's just say that we can generate the coverage based on a 56 | given usage module. We'll start with a simple object: 57 | 58 | ```javascript 59 | import clone from 'clone' 60 | clone('hello') 61 | ``` 62 | 63 | Based on this usage, a coverage report could be generated and the resulting code slice would look much easier to learn 64 | quickly: 65 | 66 | ```javascript 67 | export default clone 68 | 69 | function clone(item) { 70 | return item 71 | } 72 | ``` 73 | 74 | We've gone from `38` lines of code to `1` and the cyclomatic complexity from `10` to `1`. That's considerably more easy 75 | to learn! But that's not everything that's important in this code. The original code is definitely important. So let's 76 | add more use-cases and see how this slice is changed. 77 | 78 | ```javascript 79 | import clone from 'clone' 80 | clone('hello') 81 | clone(null) 82 | ``` 83 | 84 | With that addition of `clone(null)`, we'll get this difference: 85 | 86 | ```diff 87 | export default clone; 88 | 89 | function clone(item) { 90 | + if (!item) { 91 | + return item 92 | + } 93 | + 94 | return item 95 | } 96 | ``` 97 | 98 | That's pretty reasonable to learn in addition to what we've already learned about this code. Let's add more now: 99 | 100 | ```javascript 101 | import clone from 'clone' 102 | clone('hello') 103 | clone(null) 104 | clone({name: 'Luke'}) 105 | ``` 106 | 107 | And here's what the slice looks like now: 108 | 109 | ```diff 110 | export default clone 111 | 112 | function clone(item) { 113 | if (!item) { 114 | return item 115 | } 116 | + const type = typeof item 117 | + const isPrimitive = type !== "object" && type !== "function" 118 | + let result = item 119 | 120 | + if (!isPrimitive) { 121 | + result = {} 122 | + for (const i in item) { 123 | + result[i] = clone(item[i]) 124 | + } 125 | + } 126 | 127 | - return item 128 | + return result 129 | } 130 | ``` 131 | 132 | Let's do this one more time: 133 | 134 | ```javascript 135 | import clone from 'clone' 136 | clone('hello') 137 | clone(null) 138 | clone({name: 'Luke'}) 139 | clone({friends: [{name: 'Rebecca'}]}) 140 | ``` 141 | 142 | And with that, we add yet another edge case. 143 | 144 | ```diff 145 | export default clone 146 | 147 | function clone(item) { 148 | if (!item) { 149 | return item 150 | } 151 | const type = typeof item 152 | const string = Object.prototype.toString.call(item) 153 | const isPrimitive = type !== "object" && type !== "function" 154 | let result = item 155 | 156 | if (!isPrimitive) { 157 | - result = {} 158 | - for (const i in item) { 159 | - result[i] = clone(item[i]) 160 | + if (string === '[object Array]') { 161 | + result = [] 162 | + item.forEach((child, index, array) => { 163 | + result[index] = clone(child) 164 | + }) 165 | + } else { 166 | + result = {} 167 | + for (const i in item) { 168 | + result[i] = clone(item[i]) 169 | + } 170 | } 171 | } 172 | 173 | return result 174 | } 175 | ``` 176 | 177 | The benefit of this approach is that we learn the code use-case-by-use-case. It's much easier to learn bit by bit like 178 | this, and slice-js enables this. 179 | -------------------------------------------------------------------------------- /other/ultra-tree-shaking.md: -------------------------------------------------------------------------------- 1 | # Ultra-Tree Shaking ™ 2 | 3 | Tree shaking is a super cool concept. Here's a basic example of tree shaking from Webpack or Rollup: 4 | 5 | **math.js** 6 | 7 | ```javascript 8 | export {doMath, sayMath} 9 | 10 | const add = (a, b) => a + b 11 | const subtract = (a, b) => a - b 12 | const divide = (a, b) => a / b 13 | const multiply = (a, b) => a * b 14 | 15 | function doMath(a, b, operation) { 16 | switch (operation) { 17 | case 'add': 18 | return add(a, b) 19 | case 'subtract': 20 | return subtract(a, b) 21 | case 'divide': 22 | return divide(a, b) 23 | case 'multiply': 24 | return multiply(a, b) 25 | default: 26 | throw new Error(`Unsupported operation: ${operation}`) 27 | } 28 | } 29 | 30 | function sayMath() { 31 | return 'MATH!' 32 | } 33 | ``` 34 | 35 | **app.js** 36 | 37 | ```javascript 38 | import {doMath} 39 | doMath(2, 3, 'multiply') // 6 40 | ``` 41 | 42 | The tree-shaken result of **math.js** would effectively be: 43 | 44 | ```javascript 45 | export {doMath} 46 | 47 | const add = (a, b) => a + b 48 | const subtract = (a, b) => a - b 49 | const divide = (a, b) => a / b 50 | const multiply = (a, b) => a * b 51 | 52 | function doMath(a, b, operation) { 53 | switch (operation) { 54 | case 'add': 55 | return add(a, b) 56 | case 'subtract': 57 | return subtract(a, b) 58 | case 'divide': 59 | return divide(a, b) 60 | case 'multiply': 61 | return multiply(a, b) 62 | default: 63 | throw new Error(`Unsupported operation: ${operation}`) 64 | } 65 | } 66 | ``` 67 | 68 | However, with SliceJS, we could remove even _more_ code. Like this: 69 | 70 | ```javascript 71 | export {doMath} 72 | 73 | const multiply = (a, b) => a * b 74 | 75 | function doMath(a, b) { 76 | return multiply(a, b) 77 | } 78 | ``` 79 | 80 | Imagine doing this with `lodash`, `jquery` or `react`! Could be some pretty serious savings! 81 | 82 | The biggest challenge with this would be getting an accurate measure of code coverage. For most applications, you'd have 83 | a hard time making sure that your tests cover all use cases, and if you slice code out that's not covered by your test 84 | cases, then your users wont get that code and things will blow up. There's still more work to be done here, but I think 85 | that it's possible to make a big difference! 86 | 87 | ## Shaking data 88 | 89 | Another thing that I think would be super cool to do would be to not allocate memory for objects that are never used. 90 | Right now, with SliceJS, here's an example that could be further optimized: 91 | 92 | **log.js** 93 | 94 | ```javascript 95 | const currentLevel = 0 96 | 97 | const logLevels = { 98 | ALL: 100, 99 | DEBUG: 70, 100 | ERROR: 50, 101 | INFO: 30, 102 | WARN: 20, 103 | OFF: 0, 104 | } 105 | 106 | const setCurrentLevel = level => currentLevel = level 107 | 108 | export {log, setCurrentLevel, logLevels} 109 | 110 | function log(level, ...args) { 111 | if (currentLevel > level) { 112 | console.log(...args) 113 | } 114 | } 115 | ``` 116 | 117 | **app.js** 118 | 119 | ```javascript 120 | import {log, logLevels, setCurrentLevel} 121 | setCurrentLevel(logLevels.ERROR) 122 | log(logLevels.WARN, 'This is a warning!') 123 | ``` 124 | 125 | If we tracked data coverage (in addition to branch/function/statement coverage as we do now), then we could slice out 126 | the allocation for some of the properties in the `logLevels` object as well! This would result in: 127 | 128 | ```javascript 129 | const currentLevel = 0 130 | 131 | const logLevels = { 132 | ERROR: 50, 133 | WARN: 20, 134 | } 135 | 136 | const setCurrentLevel = level => currentLevel = level 137 | 138 | export {log, setCurrentLevel, logLevels} 139 | 140 | function log(level, ...args) { 141 | if (currentLevel > level) { 142 | console.log(...args) 143 | } 144 | } 145 | ``` 146 | 147 | Which would be even cooler in scenarios where the objects are actually significant in length and amount of memory! 148 | -------------------------------------------------------------------------------- /package-scripts.js: -------------------------------------------------------------------------------- 1 | const npsUtils = require('nps-utils') 2 | 3 | const concurrent = npsUtils.concurrent 4 | const series = npsUtils.series 5 | const oneLine = npsUtils.commonTags.oneLine 6 | 7 | module.exports = { 8 | scripts: { 9 | commit: { 10 | description: oneLine` 11 | This uses commitizen to help us generate well formatted commit messages 12 | `, 13 | script: 'git-cz', 14 | }, 15 | test: { 16 | default: 'jest --coverage', 17 | watch: 'jest --watch', 18 | }, 19 | build: { 20 | description: 'delete the dist directory and run babel to build the files', 21 | script: series( 22 | 'rimraf dist', 23 | 'babel --copy-files --out-dir dist --ignore *.test.js src' 24 | ), 25 | }, 26 | lint: { 27 | description: 'lint the entire project', 28 | script: 'eslint .', 29 | }, 30 | reportCoverage: { 31 | description: oneLine` 32 | Report coverage stats to codecov. 33 | This should be run after the \`test\` script 34 | `, 35 | script: 'codecov', 36 | }, 37 | release: { 38 | description: oneLine` 39 | We automate releases with semantic-release. 40 | This should only be run on travis 41 | `, 42 | script: 'semantic-release pre && npm publish && semantic-release post', 43 | }, 44 | validate: { 45 | description: oneLine` 46 | This runs several scripts to make sure things 47 | look good before committing or on clean install 48 | `, 49 | script: concurrent.nps('lint', 'build', 'test'), 50 | }, 51 | addContributor: { 52 | description: 'When new people contribute to the project, run this', 53 | script: 'all-contributors add', 54 | }, 55 | generateContributors: { 56 | description: 'Update the badge and contributors table', 57 | script: 'all-contributors generate', 58 | }, 59 | }, 60 | options: { 61 | silent: false, 62 | }, 63 | } 64 | 65 | // this is not transpiled 66 | /* 67 | eslint 68 | comma-dangle: [ 69 | 2, 70 | { 71 | arrays: 'always-multiline', 72 | objects: 'always-multiline', 73 | functions: 'never' 74 | } 75 | ] 76 | */ 77 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "slice-js", 3 | "version": "0.0.0-semantically-released", 4 | "private": true, 5 | "description": "A tool to generate a program slice of your JavaScript code", 6 | "main": "dist/index.js", 7 | "scripts": { 8 | "start": "nps", 9 | "test": "nps test", 10 | "commitmsg": "opt --in commit-msg --exec \"validate-commit-msg\"", 11 | "precommit": "lint-staged && opt --in pre-commit --exec \"npm start validate\"" 12 | }, 13 | "files": [ 14 | "dist" 15 | ], 16 | "keywords": [], 17 | "author": "Kent C. Dodds (http://kentcdodds.com/)", 18 | "license": "SEE LICENSE IN LICENSE", 19 | "dependencies": { 20 | "babel-core": "^6.24.0", 21 | "babel-plugin-istanbul": "^4.0.0", 22 | "babel-plugin-minify-dead-code-elimination": "^0.1.4", 23 | "indent-string": "^3.1.0", 24 | "lodash": "^4.17.4" 25 | }, 26 | "devDependencies": { 27 | "all-contributors-cli": "^4.0.1", 28 | "babel-cli": "^6.24.0", 29 | "babel-jest": "^19.0.0", 30 | "babel-polyfill": "^6.23.0", 31 | "babel-preset-node6": "^11.0.0", 32 | "babel-preset-stage-2": "^6.22.0", 33 | "babel-register": "^6.24.0", 34 | "babel-template": "^6.23.0", 35 | "codecov": "^2.0.2", 36 | "combs": "^0.0.1", 37 | "commitizen": "^2.9.6", 38 | "cz-conventional-changelog": "^2.0.0", 39 | "eslint": "^3.17.1", 40 | "eslint-config-kentcdodds": "^12.2.0", 41 | "glob": "^7.1.1", 42 | "husky": "^0.13.2", 43 | "istanbul-lib-instrument": "^1.4.2", 44 | "jest-cli": "^19.0.2", 45 | "lint-staged": "^3.4.0", 46 | "match-sorter": "^1.7.0", 47 | "nps": "^5.0.4", 48 | "nps-utils": "^1.2.0", 49 | "opt-cli": "^1.5.1", 50 | "pify": "^2.3.0", 51 | "prettier-eslint-cli": "^3.1.2", 52 | "rimraf": "^2.6.1", 53 | "semantic-release": "^6.3.2", 54 | "spawn-command": "^0.0.2", 55 | "strip-indent": "^2.0.0", 56 | "validate-commit-msg": "^2.11.2" 57 | }, 58 | "eslintConfig": { 59 | "extends": [ 60 | "kentcdodds", 61 | "kentcdodds/jest", 62 | "kentcdodds/prettier" 63 | ] 64 | }, 65 | "jest": { 66 | "testEnvironment": "jest-environment-node", 67 | "coveragePathIgnorePatterns": [ 68 | "/node_modules/", 69 | "/fixtures/", 70 | "/helpers/", 71 | "/integration/" 72 | ], 73 | "coverageThreshold": { 74 | "global": { 75 | "branches": 85, 76 | "functions": 95, 77 | "lines": 90, 78 | "statements": 90 79 | } 80 | } 81 | }, 82 | "lint-staged": { 83 | "*.js": [ 84 | "prettier-eslint --write", 85 | "git add" 86 | ] 87 | }, 88 | "config": { 89 | "commitizen": { 90 | "path": "node_modules/cz-conventional-changelog" 91 | } 92 | }, 93 | "repository": { 94 | "type": "git", 95 | "url": "https://github.com/kentcdodds/slice-js.git" 96 | }, 97 | "bugs": { 98 | "url": "https://github.com/kentcdodds/slice-js/issues" 99 | }, 100 | "homepage": "https://github.com/kentcdodds/slice-js#readme" 101 | } 102 | -------------------------------------------------------------------------------- /src/__snapshots__/index.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`./test/fixtures/module.slicer.js 1`] = ` 4 | "./test/fixtures/module.js: multiply multiplies numbers together 5 | 6 | export { sum, multiply }; 7 | 8 | function sum(a, b) { 9 | return a + b; 10 | } 11 | 12 | function multiply(a, b) { 13 | let product, i; 14 | product = 0; 15 | for (i = 0; i < b; i++) { 16 | product = sum(product, a); 17 | } 18 | return product; 19 | } 20 | " 21 | `; 22 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import indent from 'indent-string' 3 | import {getSliceAndInfo} from './slice-code/test/helpers/utils' 4 | 5 | export default sliceTest 6 | 7 | async function sliceTest(filename, name, tester) { 8 | const sourceCode = fs.readFileSync(filename, 'utf8') 9 | const {slicedCode} = await getSliceAndInfo(sourceCode, tester, filename) 10 | // eslint-disable-next-line no-console 11 | console.log( 12 | `${relativeizePath(filename)}: ${name}\n${indent(slicedCode, 4)}`, 13 | ) 14 | } 15 | 16 | function relativeizePath(absolutePath) { 17 | return absolutePath.replace(process.cwd(), '.') 18 | } 19 | -------------------------------------------------------------------------------- /src/index.test.js: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import spawn from 'spawn-command' 3 | import glob from 'glob' 4 | 5 | const BABEL_BIN_PATH = require.resolve('babel-cli/bin/babel-node') 6 | 7 | const slicers = glob.sync( 8 | path.resolve(__dirname, '../test/fixtures/**/*.slicer.js'), 9 | ) 10 | 11 | slicers.forEach(slicer => { 12 | test(relativeizePath(slicer), () => { 13 | return runBabelNode(slicer).then(stdout => { 14 | expect(stdout).toMatchSnapshot() 15 | }) 16 | }) 17 | }) 18 | 19 | function runBabelNode(args = '', cwd = process.cwd()) { 20 | const isRelative = cwd[0] !== '/' 21 | if (isRelative) { 22 | cwd = path.resolve(__dirname, cwd) 23 | } 24 | 25 | return new Promise((resolve, reject) => { 26 | let stdout = '' 27 | let stderr = '' 28 | const command = `${BABEL_BIN_PATH} ${args}` 29 | const child = spawn(command, {cwd}) 30 | 31 | child.on('error', error => { 32 | reject(error) 33 | }) 34 | 35 | child.stdout.on('data', data => { 36 | stdout += data.toString() 37 | }) 38 | 39 | child.stderr.on('data', data => { 40 | stderr += data.toString() 41 | }) 42 | 43 | child.on('close', () => { 44 | if (stderr) { 45 | reject(stderr) 46 | } else { 47 | resolve(stdout) 48 | } 49 | }) 50 | }) 51 | } 52 | 53 | function relativeizePath(absolutePath) { 54 | return absolutePath.replace(process.cwd(), '.') 55 | } 56 | -------------------------------------------------------------------------------- /src/slice-code/babel-plugin-custom-dead-code-elimination.js: -------------------------------------------------------------------------------- 1 | export default customDeadCodeElimination 2 | 3 | function customDeadCodeElimination({types: t}) { 4 | return { 5 | visitor: { 6 | AssignmentExpression(path) { 7 | const {left, right} = path.node 8 | if ( 9 | t.isIdentifier(left) && 10 | t.isIdentifier(right) && 11 | left.name === right.name 12 | ) { 13 | path.remove() 14 | } 15 | }, 16 | VariableDeclarator(path) { 17 | const id = path.get('id') 18 | if (t.isObjectPattern(id)) { 19 | id.get('properties').forEach(objectProperty => { 20 | findAndRemoveUnusedBindings( 21 | objectProperty, 22 | objectProperty.get('value'), 23 | ) 24 | }) 25 | } else if (t.isArrayPattern(id)) { 26 | id.get('elements').forEach(element => { 27 | findAndRemoveUnusedBindings(element, element) 28 | }) 29 | } else if (t.isIdentifier(id)) { 30 | const keepReferences = true // TODO, there appears to be a bug with 31 | findAndRemoveUnusedBindings(path, id, keepReferences) 32 | } else { 33 | throw new Error( 34 | ` 35 | slice-js does not yet support 36 | VariableDeclarators with an id of type ${id.type} 37 | `, 38 | ) 39 | } 40 | }, 41 | ExpressionStatement(path) { 42 | if (t.isMemberExpression(path.node.expression)) { 43 | // foo.bar; (this could break code if there 44 | // are side-effects via getters) 45 | path.remove() 46 | } 47 | }, 48 | Program: { 49 | // these are things we can only reasonably 50 | // try to remove after everything else has been removed. 51 | exit(path) { 52 | path.traverse({ 53 | ObjectPattern(objPath) { 54 | if (!objPath.node.properties.length) { 55 | // `var {} = foo` // don't ask me why.... 56 | objPath.parentPath.remove() 57 | } 58 | }, 59 | ArrayPattern(arrPath) { 60 | if (!arrPath.node.elements.length) { 61 | // `var [] = foo` // don't ask me why... 62 | arrPath.parentPath.remove() 63 | } 64 | }, 65 | }) 66 | }, 67 | }, 68 | }, 69 | } 70 | 71 | function findAndRemoveUnusedBindings(path, identifier) { 72 | if (isRemoved(path)) { 73 | return 74 | } 75 | const referencePaths = getReferencePaths(path, identifier.node.name) 76 | if (isReferencingNothingButItself(identifier.node, referencePaths)) { 77 | path.remove() 78 | } 79 | } 80 | 81 | function getReferencePaths(path, name) { 82 | const binding = path.scope.getBinding(name) || {referencePaths: []} 83 | return binding.referencePaths.filter(isNotRemoved) 84 | } 85 | 86 | function isReferencingNothingButItself(identifier, referencePaths) { 87 | return !referencePaths.length || 88 | (referencePaths.length === 1 && referencePaths[0].node === identifier) 89 | } 90 | 91 | function isRemoved(path) { 92 | // if the path has a parent that has no node, then it has been removed 93 | return !!path.find(parent => !parent.node) 94 | } 95 | 96 | function isNotRemoved(path) { 97 | return !isRemoved(path) 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/slice-code/get-sliced-code-transform.js: -------------------------------------------------------------------------------- 1 | // I know it's nuts, but it's a lot easier 2 | // to develop with ASTExplorer.net this way... 3 | /* eslint max-lines:[2, 1000] max-len:0 */ 4 | /* eslint no-negated-condition:0 */ 5 | // for development, fork this: https://astexplorer.net/#/bk7MWWZZOR 6 | // and log and copy/paste filteredCoverage and the plugin source 7 | export default getSliceCodeTransform 8 | 9 | function getSliceCodeTransform(filteredCoverage) { 10 | const {fnMap, branchMap} = filteredCoverage 11 | const removedPaths = new Set() 12 | return function sliceCodeTransform({types: t}) { 13 | return { 14 | visitor: { 15 | Program(path) { 16 | path.traverse({ 17 | enter(childPath) { 18 | t.removeComments(childPath.node) 19 | }, 20 | }) 21 | }, 22 | FunctionDeclaration: functionVisitor, 23 | FunctionExpression: functionVisitor, 24 | ArrowFunctionExpression: arrowFunctionVisitor, 25 | IfStatement(path) { 26 | if (!isBranchCovered(branchMap, path.node)) { 27 | // console.log('2922') 28 | removedPaths.add(path) 29 | path.remove() 30 | return 31 | } 32 | path.traverse({ 33 | enter(childPath) { 34 | const {key, node, parent, parentPath} = childPath 35 | const otherKey = key === 'consequent' ? 36 | 'alternate' : 37 | 'consequent' 38 | if (skipPath()) { 39 | return 40 | } 41 | const sideIsCovered = isBranchSideCovered( 42 | branchMap, 43 | key, 44 | node, 45 | parent, 46 | ) 47 | const otherSideExists = !!path.node[otherKey] 48 | const otherSideIsCovered = isBranchSideCovered( 49 | branchMap, 50 | otherKey, 51 | node, 52 | parent, 53 | ) 54 | if (isUncoveredAndMissingElse()) { 55 | handleUncoveredAndMissingElse() 56 | } else if (hasUncoveredSide()) { 57 | replaceNodeWithNodeFromParent(childPath, otherKey) 58 | } 59 | 60 | function skipPath() { 61 | return parentPath.removed || 62 | parentPath !== path || 63 | !(key === 'consequent' || key === 'alternate') 64 | } 65 | 66 | function isUncoveredAndMissingElse() { 67 | return !sideIsCovered && !otherSideExists 68 | } 69 | 70 | function handleUncoveredAndMissingElse() { 71 | if (otherSideIsCovered) { 72 | // if (foo) { /* not covered */ } (else-path doesn't exist but is covered) // result: removed 73 | // console.log('2954') 74 | path.remove() 75 | } else { 76 | // if (foo) { /* not covered */ } 77 | // (else-path doesn't exist and isn't covered) 78 | // result: ... not sure :shrug: 79 | // console.log('2959') 80 | childPath.remove() 81 | } 82 | } 83 | 84 | function hasUncoveredSide() { 85 | // if (foo) { /* not covered */ } else { /* covered */ } // result: /* covered */ 86 | // if (foo) { /* not covered */ } else { /* not covered */ } // result: removed 87 | // if (foo) { /* covered */ } (else-path doesn't exist and isn't covered) // result: /* covered */ 88 | // if (foo) { /* covered */ } else { /* not covered */ } // result: ... not sure :shrug: 89 | return ((!sideIsCovered || !otherSideExists) && 90 | !otherSideIsCovered) || 91 | (!sideIsCovered && otherSideIsCovered) 92 | } 93 | }, 94 | }) 95 | }, 96 | ConditionalExpression(path) { 97 | const branchCoverageData = getBranchCoverageData( 98 | branchMap, 99 | path.node, 100 | ) 101 | 102 | if (!branchCoverageData) { 103 | // console.log('2981') 104 | path.remove() 105 | return 106 | } 107 | path.traverse({ 108 | enter(childPath) { 109 | const {key} = childPath 110 | const otherKey = key === 'consequent' ? 111 | 'alternate' : 112 | 'consequent' 113 | if ( 114 | !childPath.removed && 115 | childPath.parentPath === path && 116 | (key === 'consequent' || key === 'alternate') && 117 | (!branchCoverageData[key] || !branchCoverageData[key].covered) 118 | ) { 119 | // console.log('2995') 120 | replaceNodeWithNodeFromParent(childPath, otherKey) 121 | } 122 | }, 123 | }) 124 | }, 125 | LogicalExpression(path) { 126 | const branchCoverageInfo = getLogicalExpressionBranchCoverageInfo( 127 | branchMap, 128 | path.node, 129 | ) 130 | if (!branchCoverageInfo) { 131 | // if there's not branch coverage info available, that could mean that this 132 | // LogicalExpression is part of another LogicalExpression, so we can skip this one 133 | return 134 | } 135 | const nodesToPreserve = getLogicalExpressionNodesToPreserve( 136 | path, 137 | branchMap, 138 | ) 139 | // console.log(nodesToPreserve) 140 | path.traverse({ 141 | enter(childPath) { 142 | if (childPath.parentPath !== path) { 143 | // we only care about direct children 144 | return 145 | } 146 | if (childPath.type === 'LogicalExpression') { 147 | handleNestedLogicalExpression(childPath) 148 | return 149 | } 150 | if (!nodesToPreserve.includes(childPath.node)) { 151 | const otherSideKey = childPath.key === 'left' ? 152 | 'right' : 153 | 'left' 154 | replaceNodeWithNodeFromParent(childPath, otherSideKey) 155 | } 156 | 157 | function handleNestedLogicalExpression(nestedExpressionPath) { 158 | const otherSideKey = nestedExpressionPath.key === 'left' ? 159 | 'right' : 160 | 'left' 161 | // if the child is a LogicalExpression, then we need to replace the parent node with only the 162 | // side of the expression that is covered. 163 | const includesLeft = isNestedLogicalExpressionIsCovered( 164 | nodesToPreserve, 165 | nestedExpressionPath.node.left, 166 | ) 167 | const includesRight = isNestedLogicalExpressionIsCovered( 168 | nodesToPreserve, 169 | nestedExpressionPath.node.right, 170 | ) 171 | if (!includesLeft && !includesRight) { 172 | // if neither side is covered, then take the parent 173 | // (LogicalExpression) and replace it with just the other side 174 | replaceNodeWithNodeFromParent( 175 | nestedExpressionPath, 176 | otherSideKey, 177 | ) 178 | return 179 | } 180 | if (includesLeft && includesRight) { 181 | // if both sides are covered, then don't replace the parent at all. This side is is needed 182 | return // eslint-disable-line no-useless-return 183 | } else if (!includesRight) { 184 | // if the right isn't covered (and the left is), 185 | // then just replace the whole LogicalExpression with the left 186 | // console.log('3045') 187 | nestedExpressionPath.replaceWith( 188 | nestedExpressionPath.node.left, 189 | ) 190 | } else { 191 | // !includesLeft 192 | // if the left isn't covered (and the right is), 193 | // then just replace the whole LogicalExpression with the right 194 | // console.log('3049') 195 | nestedExpressionPath.replaceWith( 196 | nestedExpressionPath.node.right, 197 | ) 198 | } 199 | return // eslint-disable-line no-useless-return 200 | } 201 | }, 202 | }) 203 | }, 204 | TryStatement(path) { 205 | const {statementMap} = filteredCoverage 206 | const tryBlockPath = path.get('block') 207 | const catchBlockPath = safeGet(path, 'handler.body') 208 | const finallyBlockPath = path.get('finalizer') 209 | const coveredTryStatements = getCoveredStatementsFromBlock( 210 | statementMap, 211 | tryBlockPath.node, 212 | ) 213 | const coveredCatchStatements = catchBlockPath ? 214 | getCoveredStatementsFromBlock(statementMap, catchBlockPath.node) : 215 | null 216 | const coveredFinallyStatements = getCoveredStatementsFromBlock( 217 | statementMap, 218 | finallyBlockPath.node, 219 | ) 220 | if (coveredCatchStatements && !coveredCatchStatements.length) { 221 | path.replaceWithMultiple([ 222 | ...coveredTryStatements, 223 | ...coveredFinallyStatements, 224 | ]) 225 | } else if (coveredTryStatements.length < tryBlockPath.node.body) { 226 | tryBlockPath.node.body = coveredTryStatements 227 | } 228 | }, 229 | SwitchStatement(path) { 230 | // note: SwitchStatements will always be covered. 231 | // the only time it isn't is if it's in a function/IfStatement/etc. 232 | // that isn't covered, which would be removed. So we don't need to 233 | // worry about checking whether the switch is covered. 234 | const coverageInfo = getBranchCoverageData(branchMap, path.node) 235 | path.get('cases').forEach(casePath => { 236 | if (!isCaseCovered(casePath.node)) { 237 | casePath.remove() 238 | } 239 | }) 240 | const remainingCases = path.get('cases') 241 | if (remainingCases.length === 1) { 242 | const nodesToPreserve = remainingCases[ 243 | 0 244 | ].node.consequent.filter(node => { 245 | return !t.isBreakStatement(node) 246 | }) 247 | if (!t.isIdentifier(path.node.discriminant)) { 248 | path.insertBefore(path.node.discriminant) 249 | } 250 | path.replaceWithMultiple(nodesToPreserve) 251 | } 252 | 253 | function isCaseCovered(caseNode) { 254 | const caseLocation = coverageInfo.locations.find(location => { 255 | return isLocationEqual(location, caseNode.loc) 256 | }) 257 | return !!caseLocation && caseLocation.covered 258 | } 259 | }, 260 | }, 261 | } 262 | 263 | function arrowFunctionVisitor(path) { 264 | if (!isFunctionCovered(fnMap, path.node)) { 265 | if (isFunctionReferenced(path)) { 266 | // if the function is referenced, then the best we can do is clear the body 267 | path.addComment('leading', `slice-js-coverage-ignore ignore next`) 268 | path.node.body.body = [] 269 | return 270 | } 271 | if (t.isAssignmentExpression(path.parentPath)) { 272 | path.parentPath.remove() 273 | } else if (path.parentPath.node.id) { 274 | removePathAndReferences(path, path.parentPath.node.id.name) 275 | } else { 276 | path.remove() 277 | } 278 | } 279 | } 280 | 281 | function functionVisitor(path) { 282 | /* eslint complexity:[2,6] */ 283 | if (isFunctionCovered(fnMap, path.node)) { 284 | return 285 | } 286 | if (shouldPreserveFunctionBody(path)) { 287 | // if it's detected that we should preserve the function body, then 288 | // we shouldn't do anything with the function except ignore coverage on it 289 | path.addComment('leading', `slice-js-coverage-ignore ignore next`) 290 | } else if (isFunctionReferenced(path)) { 291 | path.addComment('leading', `slice-js-coverage-ignore ignore next`) 292 | // if the function is referenced, then the best we can do is clear the body 293 | path.node.body.body = [] 294 | } else if ( 295 | t.isAssignmentExpression(path.parentPath) || 296 | t.isFunctionExpression(path) 297 | ) { 298 | path.parentPath.remove() 299 | } else { 300 | removePathAndReferences(path, path.node.id.name) 301 | } 302 | } 303 | 304 | function shouldPreserveFunctionBody(path) { 305 | const statementPath = path.getStatementParent() 306 | if (t.isReturnStatement(statementPath)) { 307 | // coveres `foo` in: function bar() { return function foo() {} } 308 | const functionParent = statementPath.getFunctionParent() 309 | return isFunctionCovered(fnMap, functionParent.node) 310 | } else { 311 | // TODO: cover more edge cases 312 | return isFunctionCovered(fnMap, path.node) 313 | } 314 | } 315 | 316 | function isFunctionReferenced(path) { 317 | if (t.isFunctionDeclaration(path)) { 318 | return isFunctionDeclarationReferenced() 319 | } else if (t.isAssignmentExpression(path.parentPath)) { 320 | return isFunctionExpressionReferenced() 321 | } else { 322 | return false 323 | } 324 | 325 | function isFunctionExpressionReferenced() { 326 | // from here on out, we're looking for something like this: 327 | // foo.bar.baz = () => {} 328 | // // then later 329 | // foo.bar.baz.buzz = 'referencing baz' 330 | const expressionStatement = path.parentPath.findParent( 331 | t.isExpressionStatement, 332 | ) 333 | const referenceChain = buildReferenceChain( 334 | expressionStatement.get('expression.left'), 335 | ) 336 | const start = expressionStatement.get('expression.left.object') 337 | const binding = path.scope.getBinding(start.node.name) 338 | if (!binding) { 339 | return false 340 | } 341 | // return whether at least one of these references is referencing the function 342 | return binding.referencePaths.some(refPath => { 343 | const expStatement = refPath.findParent(t.isExpressionStatement) 344 | if (!expStatement || !expStatement.node) { 345 | return false 346 | } 347 | const memberExpression = expStatement.get('expression.left') 348 | if (!t.isMemberExpression(memberExpression)) { 349 | return false 350 | } 351 | const refChain = buildReferenceChain(memberExpression) 352 | // refChain: [foo, bar, baz], referenceChain: [foo, bar] :+1: 353 | if (refChain < referenceChain.length) { 354 | return false 355 | } 356 | // return false if the referenceChain has anything the refChain does not 357 | // this means the refChain can be longer 358 | return !refChain.every((ref, i) => { 359 | const outOfElements = !referenceChain[i] 360 | if (outOfElements) { 361 | return true 362 | } 363 | const selfReference = ref.node === referenceChain[i].node 364 | const notAReference = ref.node.name !== referenceChain[i].node.name 365 | return selfReference || notAReference 366 | }) 367 | }) 368 | 369 | function buildReferenceChain(memberExpression) { 370 | let iterations = 0 371 | let property = memberExpression.get('property') 372 | let object = memberExpression.get('object') 373 | const chain = [property] 374 | while (t.isMemberExpression(object)) { 375 | property = object.get('property') 376 | object = object.get('object') 377 | chain.push(property) 378 | iterations++ 379 | if (iterations > 10) { 380 | throw new Error( 381 | 'slice-js avoiding infinite loop in buildReferenceChain', 382 | ) 383 | } 384 | } 385 | return chain.reverse() 386 | } 387 | } 388 | 389 | function isFunctionDeclarationReferenced() { 390 | const {name} = path.get('id').node 391 | path.scope.getBinding(name).referencePaths.every(refPath => 392 | refPath.find( 393 | parent => 394 | !t.isExportDefaultDeclaration(parent) && 395 | !t.isExportSpecifier(parent), 396 | // we don't care about references to exports 397 | )) 398 | } 399 | } 400 | 401 | function replaceNodeWithNodeFromParent(path, key) { 402 | // console.log('replaceNodeWithNodeFromParent', path, key) 403 | const {parentPath, parent} = path 404 | const replacementNode = parent[key] || path.node 405 | if (parentPath.type === 'IfStatement') { 406 | // if there are side-effects in the IfStatement, then we need to preserve those 407 | const typesToPreserve = [ 408 | 'AssignmentExpression', 409 | 'CallExpression', 410 | 'UnaryExpression', 411 | ] 412 | // these can't exist on their own and need to be wrapped in an ExpressionStatement 413 | const typesToWrap = ['CallExpression', 'UnaryExpression'] 414 | const nodesToPreserve = [] 415 | const testPath = parentPath.get('test') 416 | testPath.traverse({ 417 | enter(testChildPath) { 418 | const EXIT_EARLY = 'EXIT_EARLY' 419 | if (testChildPath.parentPath !== testPath) { 420 | // we're only concerned with direct children 421 | return 422 | } 423 | 424 | const result = handleLogicalExpression() 425 | if (result === EXIT_EARLY) { 426 | return 427 | } 428 | preserveNode() 429 | 430 | function preserveNode() { 431 | if (typesToPreserve.includes(testChildPath.node.type)) { 432 | if (typesToWrap.includes(testChildPath.node.type)) { 433 | nodesToPreserve.push( 434 | t.expressionStatement(testChildPath.node), 435 | ) 436 | } else { 437 | nodesToPreserve.push(testChildPath.node) 438 | } 439 | } 440 | } 441 | 442 | function handleLogicalExpression() { 443 | const logicalExpressionNodesToPreserve = getLogicalExpressionNodesToPreserve( 444 | testChildPath.parentPath, 445 | branchMap, 446 | ) 447 | if (testChildPath.parent.type === 'LogicalExpression') { 448 | handleNestedLogicalExpression(logicalExpressionNodesToPreserve) 449 | if ( 450 | !logicalExpressionNodesToPreserve.includes(testChildPath.node) 451 | ) { 452 | // if this part of the LogicalExpression is not covered, then we don't want to preserve it. 453 | return EXIT_EARLY 454 | } 455 | } 456 | return null 457 | } 458 | 459 | function handleNestedLogicalExpression(coveredNodes) { 460 | if (testChildPath.type !== 'LogicalExpression') { 461 | return 462 | } 463 | const includesLeft = isNestedLogicalExpressionIsCovered( 464 | coveredNodes, 465 | testChildPath.node.left, 466 | ) 467 | const includesRight = isNestedLogicalExpressionIsCovered( 468 | coveredNodes, 469 | testChildPath.node.right, 470 | ) 471 | // need to create an expression statement because we'll be removing the if statement 472 | // and this needs to be a node that can stand on its own. 473 | if (includesLeft && includesRight) { 474 | nodesToPreserve.push(t.expressionStatement(testChildPath.node)) 475 | } else if (!includesRight) { 476 | nodesToPreserve.push( 477 | t.expressionStatement(testChildPath.node.left), 478 | ) 479 | } else { 480 | // !includesLeft 481 | nodesToPreserve.push( 482 | t.expressionStatement(testChildPath.node.right), 483 | ) 484 | } 485 | } 486 | }, 487 | }) 488 | parentPath.insertBefore(nodesToPreserve) 489 | } 490 | if (replacementNode && replacementNode.body) { 491 | // console.log('3213') 492 | parentPath.replaceWithMultiple(replacementNode.body) 493 | } else if (replacementNode) { 494 | // console.log('3216') 495 | parentPath.replaceWith(replacementNode) 496 | } 497 | } 498 | 499 | function removePathAndReferences(path, name) { 500 | path.scope.getBinding(name).referencePaths.forEach(binding => { 501 | /* eslint complexity:0 */ // TODO clean this up 502 | // console.log('removing binding', binding) 503 | if (t.isExportSpecifier(binding.parent)) { 504 | removeExportSpecifierBinding(binding) 505 | } else if (t.isExportNamedDeclaration(binding)) { 506 | // console.log('3227') 507 | binding.remove() 508 | } else if (t.isCallExpression(binding.parent)) { 509 | // console.log('3230') 510 | removeCallExpressionBinding(binding) 511 | } else if (t.isMemberExpression(binding.parent)) { 512 | // console.log('I am here') 513 | // TODO, get more test cases because I'm sure we'll need to actually do something here... 514 | } else if (t.isAssignmentExpression(binding.parent)) { 515 | const expressionStatement = binding.findParent( 516 | t.isExpressionStatement, 517 | ) 518 | const sequenceExpression = binding.findParent(t.isSequenceExpression) 519 | if (expressionStatement) { 520 | expressionStatement.remove() 521 | } else if (sequenceExpression) { 522 | sequenceExpression.remove() 523 | } 524 | } else if (t.isObjectProperty(binding.parent)) { 525 | const {node: objectExpression} = binding.parentPath.parentPath 526 | const {properties} = objectExpression 527 | // console.log('3233') 528 | properties.splice(properties.indexOf(binding.parent), 1) 529 | } else { 530 | /* istanbul ignore next we have no coverage of this else... and that's the problem :) */ 531 | console.error('path', path) // eslint-disable-line no-console 532 | /* istanbul ignore next */ 533 | console.error('binding', binding) // eslint-disable-line no-console 534 | /* istanbul ignore next */ 535 | throw new Error( 536 | 'Attempting to remove a type of binding for a path that has not yet be implemented. ' + 537 | 'Please investigate how to safely remove this binding.', 538 | ) 539 | } 540 | }) 541 | // console.log('3240') 542 | if (path.parentPath.type === 'VariableDeclarator') { 543 | // console.log('3244') 544 | path.parentPath.remove() 545 | } else { 546 | // console.log('path remove', path) 547 | // console.log('3248') 548 | path.remove() 549 | } 550 | 551 | function removeExportSpecifierBinding(binding) { 552 | const {parentPath: {parent: {specifiers}}} = binding 553 | const specifierIndex = specifiers.indexOf(binding.parent) 554 | // no need to check whether index is -1. It's definitely in there. 555 | specifiers.splice(specifierIndex, 1) 556 | } 557 | 558 | function removeCallExpressionBinding(binding) { 559 | // console.log('removeCallExpressionBinding(binding)', binding) 560 | // console.log(binding.scope.getBinding(binding.node.name).referencePaths) 561 | const {parentPath: callPath} = binding 562 | const {parentPath: usePath} = callPath 563 | const removedNode = binding.findParent(parentPath => 564 | removedPaths.has(parentPath)) 565 | if (removedNode) { 566 | // no need to remove any children 567 | return 568 | } 569 | if (usePath.type === 'LogicalExpression') { 570 | const otherSideOfLogicalExpressionKey = callPath.key === 'left' ? 571 | 'right' : 572 | 'left' 573 | // console.log('3266') 574 | usePath.replaceWith(usePath.node[otherSideOfLogicalExpressionKey]) 575 | } else if (usePath.type === 'ConditionalExpression') { 576 | removeConditionalExpressionSide(callPath) 577 | } else { 578 | // console.log('3269', usePath.getSource(), usePath) 579 | usePath.remove() 580 | } 581 | } 582 | 583 | function removeConditionalExpressionSide(condPath) { 584 | const {key} = condPath 585 | const otherKey = key === 'consequent' ? 'alternate' : 'consequent' 586 | if ( 587 | !condPath.removed && 588 | condPath.parentPath === path && 589 | (key === 'consequent' || key === 'alternate') 590 | ) { 591 | // console.log('3266') 592 | replaceNodeWithNodeFromParent(condPath, otherKey) 593 | } 594 | } 595 | } 596 | } 597 | } 598 | 599 | function getFunctionCoverageData(fnLocs, {body: {loc: srcLoc}}) { 600 | const fnCov = Object.keys(fnLocs) 601 | .map(key => fnLocs[key]) 602 | .find(({loc}) => isLocationEqual(loc, srcLoc)) 603 | return fnCov 604 | } 605 | 606 | function isFunctionCovered(fnLocs, node) { 607 | return !!getFunctionCoverageData(fnLocs, node) 608 | } 609 | 610 | function isBranchCovered(branches, node) { 611 | const branchCoverageData = getBranchCoverageData(branches, node) 612 | return !!branchCoverageData 613 | } 614 | 615 | function getBranchCoverageData(branches, node) { 616 | const typeMap = { 617 | if: 'IfStatement', 618 | 'cond-expr': 'ConditionalExpression', 619 | 'binary-expr': 'LogicalExpression', 620 | switch: 'SwitchStatement', 621 | } 622 | const index = Object.keys(branches).find(key => { 623 | const branch = branches[key] 624 | if (typeMap[branch.type] !== node.type) { 625 | return false 626 | } 627 | return isLocationEqual(branch.loc, node.loc) 628 | }) 629 | return branches[index] 630 | } 631 | 632 | function isBranchSideCovered(branches, side, node, parentNode) { 633 | const branch = getBranchCoverageData(branches, parentNode) 634 | if (!branch || !branch[side]) { 635 | return false 636 | } 637 | return branch[side].covered 638 | } 639 | 640 | function getLogicalExpressionBranchCoverageInfo(branches, node) { 641 | return Object.keys(branches) 642 | .map(key => branches[key]) 643 | .filter(branch => branch.type === 'binary-expr') 644 | .find(branch => isLocationEqual(node.loc, branch.loc)) 645 | } 646 | 647 | function isNestedLogicalExpressionIsCovered(coveredNodes, node) { 648 | if (node.type === 'LogicalExpression') { 649 | return isNestedLogicalExpressionIsCovered(coveredNodes, node.left) || 650 | isNestedLogicalExpressionIsCovered(coveredNodes, node.right) 651 | } else { 652 | return coveredNodes.includes(node) 653 | } 654 | } 655 | 656 | function isLocationEqual(loc1, loc2) { 657 | if (!loc1 || !loc2) { 658 | return false 659 | } 660 | const isEqual = isLineColumnEqual(loc1.start, loc2.start) && 661 | isLineColumnEqual(loc1.end, loc2.end) 662 | return isEqual 663 | } 664 | 665 | function isLineColumnEqual(obj1, obj2) { 666 | return obj1.line === obj2.line && obj1.column === obj2.column 667 | } 668 | 669 | function getCoveredStatementsFromBlock(coveredStatements, blockNode) { 670 | if (!blockNode) { 671 | return [] 672 | } 673 | return blockNode.body.reduce( 674 | (allStatements, statement) => { 675 | if (isStatementCovered(coveredStatements, statement)) { 676 | allStatements.push(statement) 677 | } 678 | return allStatements 679 | }, 680 | [], 681 | ) 682 | } 683 | 684 | function isStatementCovered(coveredStatements, statement) { 685 | return Object.keys(coveredStatements).find(s => { 686 | const coveredLoc = coveredStatements[s] 687 | return isLocationEqual(coveredLoc, statement.loc) 688 | }) 689 | } 690 | 691 | function getLogicalExpressionNodesToPreserve(path, branchMap) { 692 | const branchCoverageInfo = getLogicalExpressionBranchCoverageInfo( 693 | branchMap, 694 | path.node, 695 | ) 696 | // console.log('branchCoverageInfo', branchCoverageInfo) 697 | if (!branchCoverageInfo) { 698 | // if there's not branch coverage info available, that could mean that this 699 | // LogicalExpression is part of another LogicalExpression, so we can skip this one 700 | return null 701 | } 702 | const nodesToPreserve = [] 703 | path.traverse({ 704 | enter(childPath) { 705 | const location = branchCoverageInfo.locations.find(loc => 706 | isLocationEqual(loc, childPath.node.loc)) 707 | if (location && location.covered) { 708 | nodesToPreserve.push(childPath.node) 709 | } 710 | }, 711 | }) 712 | return nodesToPreserve 713 | } 714 | 715 | function safeGet(path, getPath) { 716 | try { 717 | return path.get(getPath) 718 | } catch (e) { 719 | return null 720 | } 721 | } 722 | -------------------------------------------------------------------------------- /src/slice-code/index.js: -------------------------------------------------------------------------------- 1 | import * as babel from 'babel-core' 2 | import deadCodeElimination from 'babel-plugin-minify-dead-code-elimination' 3 | import customDeadCodeElimination 4 | from './babel-plugin-custom-dead-code-elimination' 5 | import transformCoverage from './transform-coverage' 6 | import getSliceCodeTransform from './get-sliced-code-transform' 7 | 8 | export {sliceCodeAndGetInfo, sliceCode as default} 9 | 10 | function sliceCode(sourceCode, coverageData) { 11 | // console.log('coverageData', JSON.stringify(coverageData, null, 2)) 12 | const filteredCoverage = transformCoverage(coverageData) 13 | // console.log('filteredCoverage', JSON.stringify(filteredCoverage, null, 2)) 14 | // console.log('\n\n\n\nsourceCode\n', sourceCode) 15 | return sliceCodeFromFilteredCoverage(sourceCode, filteredCoverage) 16 | } 17 | 18 | function sliceCodeAndGetInfo(sourceCode, coverageData) { 19 | // console.log('coverageData', JSON.stringify(coverageData, null, 2)) 20 | const filteredCoverage = transformCoverage(coverageData) 21 | // console.log('filteredCoverage', JSON.stringify(filteredCoverage, null, 2)) 22 | // console.log('\n\n\n\nsourceCode\n', sourceCode) 23 | let slice, error 24 | try { 25 | slice = sliceCodeFromFilteredCoverage(sourceCode, filteredCoverage) 26 | } catch (e) { 27 | error = e 28 | } 29 | return {slice, error, filteredCoverage} 30 | } 31 | 32 | function sliceCodeFromFilteredCoverage(sourceCode, filteredCoverage) { 33 | const {path: filename} = filteredCoverage 34 | const commonOptions = { 35 | filename, 36 | babelrc: false, 37 | } 38 | const {code: sliced} = babel.transform(sourceCode, { 39 | ...commonOptions, 40 | plugins: [getSliceCodeTransform(filteredCoverage)], 41 | }) 42 | // console.log('sliced', sliced) 43 | // TODO: perf - save time parsing by just transforming the 44 | // AST from the previous run 45 | // This will probably significantly speed things up. 46 | // Unfortunately, when I tried the first time, 47 | // I couldn't get it working :shrug: 48 | const {code: deadCodeEliminated} = babel.transform(sliced, { 49 | ...commonOptions, 50 | plugins: [deadCodeElimination], 51 | }) 52 | // console.log('deadCodeEliminated', deadCodeEliminated) 53 | const {code: customDeadCodeElimiated} = babel.transform(deadCodeEliminated, { 54 | ...commonOptions, 55 | plugins: [customDeadCodeElimination], 56 | }) 57 | // console.log('customDeadCodeElimiated', customDeadCodeElimiated) 58 | return customDeadCodeElimiated 59 | } 60 | -------------------------------------------------------------------------------- /src/slice-code/test/__snapshots__/assignment-expression.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`divideEqual(15, 5) 1`] = ` 4 | "export { divideEqual }; 5 | 6 | function divideEqual(thingOne, thingTwo) { 7 | thingOne /= thingTwo; 8 | return thingOne; 9 | }" 10 | `; 11 | 12 | exports[`minusEqual(15, 5) 1`] = ` 13 | "export { minusEqual }; 14 | 15 | function minusEqual(thingOne, thingTwo) { 16 | thingOne -= thingTwo; 17 | return thingOne; 18 | }" 19 | `; 20 | 21 | exports[`multiplyEqual(15, 5) 1`] = ` 22 | "export { multiplyEqual }; 23 | 24 | function multiplyEqual(thingOne, thingTwo) { 25 | thingOne *= thingTwo; 26 | return thingOne; 27 | }" 28 | `; 29 | 30 | exports[`plusEqual(15, 5) 1`] = ` 31 | "export { plusEqual }; 32 | 33 | function plusEqual(thingOne, thingTwo) { 34 | thingOne += thingTwo; 35 | return thingOne; 36 | }" 37 | `; 38 | -------------------------------------------------------------------------------- /src/slice-code/test/__snapshots__/assignment-ternary.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`assignmentTernary("a") && assignmentTernary("b") 1`] = ` 4 | "export { assignmentTernary }; 5 | 6 | const output = { 7 | map: { 8 | a: 'A' 9 | } 10 | }; 11 | 12 | function assignmentTernary(letter) { 13 | let string = 'init'; 14 | string += letter in output.map ? output.map[letter] : letter; 15 | return string; 16 | }" 17 | `; 18 | 19 | exports[`assignmentTernary("a") 1`] = ` 20 | "export { assignmentTernary }; 21 | 22 | const output = { 23 | map: { 24 | a: 'A' 25 | } 26 | }; 27 | 28 | function assignmentTernary(letter) { 29 | let string = 'init'; 30 | string += output.map[letter]; 31 | return string; 32 | }" 33 | `; 34 | 35 | exports[`assignmentTernary("b") 1`] = ` 36 | "export { assignmentTernary }; 37 | 38 | function assignmentTernary(letter) { 39 | let string = 'init'; 40 | string += letter; 41 | return string; 42 | }" 43 | `; 44 | 45 | exports[`diacriticsClean("Some Google") 1`] = ` 46 | "export { diacriticsClean }; 47 | 48 | function diacriticsClean(input) { 49 | 50 | var string = ''; 51 | var letters = input.split(''); 52 | var index = 0; 53 | var length = letters.length; 54 | var letter; 55 | 56 | for (; index < length; index++) { 57 | letter = letters[index]; 58 | string += letter; 59 | } 60 | 61 | return string; 62 | }" 63 | `; 64 | 65 | exports[`diacriticsClean("Some apple") && diacriticsClean("Some Google") 1`] = ` 66 | "export { diacriticsClean }; 67 | 68 | const output = { 69 | map: { 70 | a: 'A' 71 | } 72 | }; 73 | 74 | function diacriticsClean(input) { 75 | 76 | var string = ''; 77 | var letters = input.split(''); 78 | var index = 0; 79 | var length = letters.length; 80 | var letter; 81 | 82 | for (; index < length; index++) { 83 | letter = letters[index]; 84 | string += letter in output.map ? output.map[letter] : letter; 85 | } 86 | 87 | return string; 88 | }" 89 | `; 90 | 91 | exports[`diacriticsClean("Some apple") 1`] = ` 92 | "export { diacriticsClean }; 93 | 94 | const output = { 95 | map: { 96 | a: 'A' 97 | } 98 | }; 99 | 100 | function diacriticsClean(input) { 101 | 102 | var string = ''; 103 | var letters = input.split(''); 104 | var index = 0; 105 | var length = letters.length; 106 | var letter; 107 | 108 | for (; index < length; index++) { 109 | letter = letters[index]; 110 | string += letter in output.map ? output.map[letter] : letter; 111 | } 112 | 113 | return string; 114 | }" 115 | `; 116 | -------------------------------------------------------------------------------- /src/slice-code/test/__snapshots__/async-code.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`callPromise(false) && callPromise(true) 1`] = ` 4 | "export { callPromise }; 5 | 6 | function promise(pass) { 7 | return new Promise((resolve, reject) => { 8 | if (pass) { 9 | resolve('pass'); 10 | } else { 11 | reject('!pass'); 12 | } 13 | }); 14 | } 15 | 16 | function callPromise(pass) { 17 | return promise(pass).catch(rejection => rejection); 18 | }" 19 | `; 20 | 21 | exports[`callPromise(false) 1`] = ` 22 | "export { callPromise }; 23 | 24 | function promise() { 25 | return new Promise((resolve, reject) => { 26 | reject('!pass'); 27 | }); 28 | } 29 | 30 | function callPromise(pass) { 31 | return promise(pass).catch(rejection => rejection); 32 | }" 33 | `; 34 | 35 | exports[`callPromise(true) 1`] = ` 36 | "export { callPromise }; 37 | 38 | function promise() { 39 | return new Promise(resolve => { 40 | resolve('pass'); 41 | }); 42 | } 43 | 44 | function callPromise(pass) { 45 | return promise(pass).catch(); 46 | }" 47 | `; 48 | 49 | exports[`callback(false) && callback(true) 1`] = ` 50 | "export { callPromise, callback }; 51 | 52 | function promise(pass) { 53 | return new Promise((resolve, reject) => { 54 | if (pass) { 55 | resolve('pass'); 56 | } else { 57 | reject('!pass'); 58 | } 59 | }); 60 | } 61 | 62 | function callback(pass, cb) { 63 | return callPromise(pass).then(cb, cb); 64 | } 65 | 66 | function callPromise(pass) { 67 | return promise(pass).catch(rejection => rejection); 68 | }" 69 | `; 70 | 71 | exports[`callback(false) 1`] = ` 72 | "export { callPromise, callback }; 73 | 74 | function promise() { 75 | return new Promise((resolve, reject) => { 76 | reject('!pass'); 77 | }); 78 | } 79 | 80 | function callback(pass, cb) { 81 | return callPromise(pass).then(cb, cb); 82 | } 83 | 84 | function callPromise(pass) { 85 | return promise(pass).catch(rejection => rejection); 86 | }" 87 | `; 88 | 89 | exports[`callback(true) 1`] = ` 90 | "export { callPromise, callback }; 91 | 92 | function promise() { 93 | return new Promise(resolve => { 94 | resolve('pass'); 95 | }); 96 | } 97 | 98 | function callback(pass, cb) { 99 | return callPromise(pass).then(cb, cb); 100 | } 101 | 102 | function callPromise(pass) { 103 | return promise(pass).catch(); 104 | }" 105 | `; 106 | -------------------------------------------------------------------------------- /src/slice-code/test/__snapshots__/clone.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`clone("hello") && clone({"friends":[{"name":"Rebecca"}]}) && clone({"title":"How to Train Your Dragon","releaseDate":"2010-03-26T06:00:00.000Z"}) 1`] = ` 4 | "export default clone; 5 | 6 | function clone(item) { 7 | const type = typeof item; 8 | const string = Object.prototype.toString.call(item); 9 | 10 | let result = item; 11 | 12 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 13 | if (string === '[object Array]') { 14 | result = []; 15 | item.forEach((child, index) => { 16 | result[index] = clone(child); 17 | }); 18 | } else { 19 | if (string === '[object Date]') { 20 | result = new Date(item); 21 | } else { 22 | result = {}; 23 | for (const i in item) { 24 | result[i] = clone(item[i]); 25 | } 26 | } 27 | } 28 | } 29 | 30 | return result; 31 | }" 32 | `; 33 | 34 | exports[`clone("hello") && clone({"friends":[{"name":"Rebecca"}]}) 1`] = ` 35 | "export default clone; 36 | 37 | function clone(item) { 38 | const type = typeof item; 39 | const string = Object.prototype.toString.call(item); 40 | 41 | let result = item; 42 | 43 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 44 | if (string === '[object Array]') { 45 | result = []; 46 | item.forEach((child, index) => { 47 | result[index] = clone(child); 48 | }); 49 | } else { 50 | result = {}; 51 | for (const i in item) { 52 | result[i] = clone(item[i]); 53 | } 54 | } 55 | } 56 | 57 | return result; 58 | }" 59 | `; 60 | 61 | exports[`clone("hello") && clone({"name":"Luke"}) && clone({"friends":[{"name":"Rebecca"}]}) && clone({"title":"How to Train Your Dragon","releaseDate":"2010-03-26T06:00:00.000Z"}) 1`] = ` 62 | "export default clone; 63 | 64 | function clone(item) { 65 | const type = typeof item; 66 | const string = Object.prototype.toString.call(item); 67 | 68 | let result = item; 69 | 70 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 71 | if (string === '[object Array]') { 72 | result = []; 73 | item.forEach((child, index) => { 74 | result[index] = clone(child); 75 | }); 76 | } else { 77 | if (string === '[object Date]') { 78 | result = new Date(item); 79 | } else { 80 | result = {}; 81 | for (const i in item) { 82 | result[i] = clone(item[i]); 83 | } 84 | } 85 | } 86 | } 87 | 88 | return result; 89 | }" 90 | `; 91 | 92 | exports[`clone("hello") && clone({"name":"Luke"}) && clone({"friends":[{"name":"Rebecca"}]}) 1`] = ` 93 | "export default clone; 94 | 95 | function clone(item) { 96 | const type = typeof item; 97 | const string = Object.prototype.toString.call(item); 98 | 99 | let result = item; 100 | 101 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 102 | if (string === '[object Array]') { 103 | result = []; 104 | item.forEach((child, index) => { 105 | result[index] = clone(child); 106 | }); 107 | } else { 108 | result = {}; 109 | for (const i in item) { 110 | result[i] = clone(item[i]); 111 | } 112 | } 113 | } 114 | 115 | return result; 116 | }" 117 | `; 118 | 119 | exports[`clone("hello") && clone({"name":"Luke"}) && clone({"title":"How to Train Your Dragon","releaseDate":"2010-03-26T06:00:00.000Z"}) 1`] = ` 120 | "export default clone; 121 | 122 | function clone(item) { 123 | const type = typeof item; 124 | const string = Object.prototype.toString.call(item); 125 | 126 | let result = item; 127 | 128 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 129 | if (string === '[object Date]') { 130 | result = new Date(item); 131 | } else { 132 | result = {}; 133 | for (const i in item) { 134 | result[i] = clone(item[i]); 135 | } 136 | } 137 | } 138 | 139 | return result; 140 | }" 141 | `; 142 | 143 | exports[`clone("hello") && clone({"name":"Luke"}) 1`] = ` 144 | "export default clone; 145 | 146 | function clone(item) { 147 | const type = typeof item; 148 | Object.prototype.toString.call(item); 149 | 150 | let result = item; 151 | 152 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 153 | result = {}; 154 | for (const i in item) { 155 | result[i] = clone(item[i]); 156 | } 157 | } 158 | 159 | return result; 160 | }" 161 | `; 162 | 163 | exports[`clone("hello") && clone({"title":"How to Train Your Dragon","releaseDate":"2010-03-26T06:00:00.000Z"}) 1`] = ` 164 | "export default clone; 165 | 166 | function clone(item) { 167 | const type = typeof item; 168 | const string = Object.prototype.toString.call(item); 169 | 170 | let result = item; 171 | 172 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 173 | if (string === '[object Date]') { 174 | result = new Date(item); 175 | } else { 176 | result = {}; 177 | for (const i in item) { 178 | result[i] = clone(item[i]); 179 | } 180 | } 181 | } 182 | 183 | return result; 184 | }" 185 | `; 186 | 187 | exports[`clone("hello") && clone(null) && clone({"friends":[{"name":"Rebecca"}]}) && clone({"title":"How to Train Your Dragon","releaseDate":"2010-03-26T06:00:00.000Z"}) 1`] = ` 188 | "export default clone; 189 | 190 | function clone(item) { 191 | if (!item) { 192 | return item; 193 | } 194 | const type = typeof item; 195 | const string = Object.prototype.toString.call(item); 196 | 197 | let result = item; 198 | 199 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 200 | if (string === '[object Array]') { 201 | result = []; 202 | item.forEach((child, index) => { 203 | result[index] = clone(child); 204 | }); 205 | } else { 206 | if (string === '[object Date]') { 207 | result = new Date(item); 208 | } else { 209 | result = {}; 210 | for (const i in item) { 211 | result[i] = clone(item[i]); 212 | } 213 | } 214 | } 215 | } 216 | 217 | return result; 218 | }" 219 | `; 220 | 221 | exports[`clone("hello") && clone(null) && clone({"friends":[{"name":"Rebecca"}]}) 1`] = ` 222 | "export default clone; 223 | 224 | function clone(item) { 225 | if (!item) { 226 | return item; 227 | } 228 | const type = typeof item; 229 | const string = Object.prototype.toString.call(item); 230 | 231 | let result = item; 232 | 233 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 234 | if (string === '[object Array]') { 235 | result = []; 236 | item.forEach((child, index) => { 237 | result[index] = clone(child); 238 | }); 239 | } else { 240 | result = {}; 241 | for (const i in item) { 242 | result[i] = clone(item[i]); 243 | } 244 | } 245 | } 246 | 247 | return result; 248 | }" 249 | `; 250 | 251 | exports[`clone("hello") && clone(null) && clone({"name":"Luke"}) && clone({"friends":[{"name":"Rebecca"}]}) && clone({"title":"How to Train Your Dragon","releaseDate":"2010-03-26T06:00:00.000Z"}) 1`] = ` 252 | "export default clone; 253 | 254 | function clone(item) { 255 | if (!item) { 256 | return item; 257 | } 258 | const type = typeof item; 259 | const string = Object.prototype.toString.call(item); 260 | 261 | let result = item; 262 | 263 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 264 | if (string === '[object Array]') { 265 | result = []; 266 | item.forEach((child, index) => { 267 | result[index] = clone(child); 268 | }); 269 | } else { 270 | if (string === '[object Date]') { 271 | result = new Date(item); 272 | } else { 273 | result = {}; 274 | for (const i in item) { 275 | result[i] = clone(item[i]); 276 | } 277 | } 278 | } 279 | } 280 | 281 | return result; 282 | }" 283 | `; 284 | 285 | exports[`clone("hello") && clone(null) && clone({"name":"Luke"}) && clone({"friends":[{"name":"Rebecca"}]}) 1`] = ` 286 | "export default clone; 287 | 288 | function clone(item) { 289 | if (!item) { 290 | return item; 291 | } 292 | const type = typeof item; 293 | const string = Object.prototype.toString.call(item); 294 | 295 | let result = item; 296 | 297 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 298 | if (string === '[object Array]') { 299 | result = []; 300 | item.forEach((child, index) => { 301 | result[index] = clone(child); 302 | }); 303 | } else { 304 | result = {}; 305 | for (const i in item) { 306 | result[i] = clone(item[i]); 307 | } 308 | } 309 | } 310 | 311 | return result; 312 | }" 313 | `; 314 | 315 | exports[`clone("hello") && clone(null) && clone({"name":"Luke"}) && clone({"title":"How to Train Your Dragon","releaseDate":"2010-03-26T06:00:00.000Z"}) 1`] = ` 316 | "export default clone; 317 | 318 | function clone(item) { 319 | if (!item) { 320 | return item; 321 | } 322 | const type = typeof item; 323 | const string = Object.prototype.toString.call(item); 324 | 325 | let result = item; 326 | 327 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 328 | if (string === '[object Date]') { 329 | result = new Date(item); 330 | } else { 331 | result = {}; 332 | for (const i in item) { 333 | result[i] = clone(item[i]); 334 | } 335 | } 336 | } 337 | 338 | return result; 339 | }" 340 | `; 341 | 342 | exports[`clone("hello") && clone(null) && clone({"name":"Luke"}) 1`] = ` 343 | "export default clone; 344 | 345 | function clone(item) { 346 | if (!item) { 347 | return item; 348 | } 349 | const type = typeof item; 350 | Object.prototype.toString.call(item); 351 | 352 | let result = item; 353 | 354 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 355 | result = {}; 356 | for (const i in item) { 357 | result[i] = clone(item[i]); 358 | } 359 | } 360 | 361 | return result; 362 | }" 363 | `; 364 | 365 | exports[`clone("hello") && clone(null) && clone({"title":"How to Train Your Dragon","releaseDate":"2010-03-26T06:00:00.000Z"}) 1`] = ` 366 | "export default clone; 367 | 368 | function clone(item) { 369 | if (!item) { 370 | return item; 371 | } 372 | const type = typeof item; 373 | const string = Object.prototype.toString.call(item); 374 | 375 | let result = item; 376 | 377 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 378 | if (string === '[object Date]') { 379 | result = new Date(item); 380 | } else { 381 | result = {}; 382 | for (const i in item) { 383 | result[i] = clone(item[i]); 384 | } 385 | } 386 | } 387 | 388 | return result; 389 | }" 390 | `; 391 | 392 | exports[`clone("hello") && clone(null) 1`] = ` 393 | "export default clone; 394 | 395 | function clone(item) { 396 | if (!item) { 397 | return item; 398 | } 399 | 400 | Object.prototype.toString.call(item); 401 | 402 | return item; 403 | }" 404 | `; 405 | 406 | exports[`clone("hello") 1`] = ` 407 | "export default clone; 408 | 409 | function clone(item) { 410 | Object.prototype.toString.call(item); 411 | 412 | return item; 413 | }" 414 | `; 415 | 416 | exports[`clone({"friends":[{"name":"Rebecca"}]}) && clone({"title":"How to Train Your Dragon","releaseDate":"2010-03-26T06:00:00.000Z"}) 1`] = ` 417 | "export default clone; 418 | 419 | function clone(item) { 420 | const type = typeof item; 421 | const string = Object.prototype.toString.call(item); 422 | 423 | let result = item; 424 | 425 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 426 | if (string === '[object Array]') { 427 | result = []; 428 | item.forEach((child, index) => { 429 | result[index] = clone(child); 430 | }); 431 | } else { 432 | if (string === '[object Date]') { 433 | result = new Date(item); 434 | } else { 435 | result = {}; 436 | for (const i in item) { 437 | result[i] = clone(item[i]); 438 | } 439 | } 440 | } 441 | } 442 | 443 | return result; 444 | }" 445 | `; 446 | 447 | exports[`clone({"friends":[{"name":"Rebecca"}]}) 1`] = ` 448 | "export default clone; 449 | 450 | function clone(item) { 451 | const type = typeof item; 452 | const string = Object.prototype.toString.call(item); 453 | 454 | let result = item; 455 | 456 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 457 | if (string === '[object Array]') { 458 | result = []; 459 | item.forEach((child, index) => { 460 | result[index] = clone(child); 461 | }); 462 | } else { 463 | result = {}; 464 | for (const i in item) { 465 | result[i] = clone(item[i]); 466 | } 467 | } 468 | } 469 | 470 | return result; 471 | }" 472 | `; 473 | 474 | exports[`clone({"name":"Luke"}) && clone({"friends":[{"name":"Rebecca"}]}) && clone({"title":"How to Train Your Dragon","releaseDate":"2010-03-26T06:00:00.000Z"}) 1`] = ` 475 | "export default clone; 476 | 477 | function clone(item) { 478 | const type = typeof item; 479 | const string = Object.prototype.toString.call(item); 480 | 481 | let result = item; 482 | 483 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 484 | if (string === '[object Array]') { 485 | result = []; 486 | item.forEach((child, index) => { 487 | result[index] = clone(child); 488 | }); 489 | } else { 490 | if (string === '[object Date]') { 491 | result = new Date(item); 492 | } else { 493 | result = {}; 494 | for (const i in item) { 495 | result[i] = clone(item[i]); 496 | } 497 | } 498 | } 499 | } 500 | 501 | return result; 502 | }" 503 | `; 504 | 505 | exports[`clone({"name":"Luke"}) && clone({"friends":[{"name":"Rebecca"}]}) 1`] = ` 506 | "export default clone; 507 | 508 | function clone(item) { 509 | const type = typeof item; 510 | const string = Object.prototype.toString.call(item); 511 | 512 | let result = item; 513 | 514 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 515 | if (string === '[object Array]') { 516 | result = []; 517 | item.forEach((child, index) => { 518 | result[index] = clone(child); 519 | }); 520 | } else { 521 | result = {}; 522 | for (const i in item) { 523 | result[i] = clone(item[i]); 524 | } 525 | } 526 | } 527 | 528 | return result; 529 | }" 530 | `; 531 | 532 | exports[`clone({"name":"Luke"}) && clone({"title":"How to Train Your Dragon","releaseDate":"2010-03-26T06:00:00.000Z"}) 1`] = ` 533 | "export default clone; 534 | 535 | function clone(item) { 536 | const type = typeof item; 537 | const string = Object.prototype.toString.call(item); 538 | 539 | let result = item; 540 | 541 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 542 | if (string === '[object Date]') { 543 | result = new Date(item); 544 | } else { 545 | result = {}; 546 | for (const i in item) { 547 | result[i] = clone(item[i]); 548 | } 549 | } 550 | } 551 | 552 | return result; 553 | }" 554 | `; 555 | 556 | exports[`clone({"name":"Luke"}) 1`] = ` 557 | "export default clone; 558 | 559 | function clone(item) { 560 | const type = typeof item; 561 | Object.prototype.toString.call(item); 562 | 563 | let result = item; 564 | 565 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 566 | result = {}; 567 | for (const i in item) { 568 | result[i] = clone(item[i]); 569 | } 570 | } 571 | 572 | return result; 573 | }" 574 | `; 575 | 576 | exports[`clone({"title":"How to Train Your Dragon","releaseDate":"2010-03-26T06:00:00.000Z"}) 1`] = ` 577 | "export default clone; 578 | 579 | function clone(item) { 580 | const type = typeof item; 581 | const string = Object.prototype.toString.call(item); 582 | 583 | let result = item; 584 | 585 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 586 | if (string === '[object Date]') { 587 | result = new Date(item); 588 | } else { 589 | result = {}; 590 | for (const i in item) { 591 | result[i] = clone(item[i]); 592 | } 593 | } 594 | } 595 | 596 | return result; 597 | }" 598 | `; 599 | 600 | exports[`clone(null) && clone({"friends":[{"name":"Rebecca"}]}) && clone({"title":"How to Train Your Dragon","releaseDate":"2010-03-26T06:00:00.000Z"}) 1`] = ` 601 | "export default clone; 602 | 603 | function clone(item) { 604 | if (!item) { 605 | return item; 606 | } 607 | const type = typeof item; 608 | const string = Object.prototype.toString.call(item); 609 | 610 | let result = item; 611 | 612 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 613 | if (string === '[object Array]') { 614 | result = []; 615 | item.forEach((child, index) => { 616 | result[index] = clone(child); 617 | }); 618 | } else { 619 | if (string === '[object Date]') { 620 | result = new Date(item); 621 | } else { 622 | result = {}; 623 | for (const i in item) { 624 | result[i] = clone(item[i]); 625 | } 626 | } 627 | } 628 | } 629 | 630 | return result; 631 | }" 632 | `; 633 | 634 | exports[`clone(null) && clone({"friends":[{"name":"Rebecca"}]}) 1`] = ` 635 | "export default clone; 636 | 637 | function clone(item) { 638 | if (!item) { 639 | return item; 640 | } 641 | const type = typeof item; 642 | const string = Object.prototype.toString.call(item); 643 | 644 | let result = item; 645 | 646 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 647 | if (string === '[object Array]') { 648 | result = []; 649 | item.forEach((child, index) => { 650 | result[index] = clone(child); 651 | }); 652 | } else { 653 | result = {}; 654 | for (const i in item) { 655 | result[i] = clone(item[i]); 656 | } 657 | } 658 | } 659 | 660 | return result; 661 | }" 662 | `; 663 | 664 | exports[`clone(null) && clone({"name":"Luke"}) && clone({"friends":[{"name":"Rebecca"}]}) && clone({"title":"How to Train Your Dragon","releaseDate":"2010-03-26T06:00:00.000Z"}) 1`] = ` 665 | "export default clone; 666 | 667 | function clone(item) { 668 | if (!item) { 669 | return item; 670 | } 671 | const type = typeof item; 672 | const string = Object.prototype.toString.call(item); 673 | 674 | let result = item; 675 | 676 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 677 | if (string === '[object Array]') { 678 | result = []; 679 | item.forEach((child, index) => { 680 | result[index] = clone(child); 681 | }); 682 | } else { 683 | if (string === '[object Date]') { 684 | result = new Date(item); 685 | } else { 686 | result = {}; 687 | for (const i in item) { 688 | result[i] = clone(item[i]); 689 | } 690 | } 691 | } 692 | } 693 | 694 | return result; 695 | }" 696 | `; 697 | 698 | exports[`clone(null) && clone({"name":"Luke"}) && clone({"friends":[{"name":"Rebecca"}]}) 1`] = ` 699 | "export default clone; 700 | 701 | function clone(item) { 702 | if (!item) { 703 | return item; 704 | } 705 | const type = typeof item; 706 | const string = Object.prototype.toString.call(item); 707 | 708 | let result = item; 709 | 710 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 711 | if (string === '[object Array]') { 712 | result = []; 713 | item.forEach((child, index) => { 714 | result[index] = clone(child); 715 | }); 716 | } else { 717 | result = {}; 718 | for (const i in item) { 719 | result[i] = clone(item[i]); 720 | } 721 | } 722 | } 723 | 724 | return result; 725 | }" 726 | `; 727 | 728 | exports[`clone(null) && clone({"name":"Luke"}) && clone({"title":"How to Train Your Dragon","releaseDate":"2010-03-26T06:00:00.000Z"}) 1`] = ` 729 | "export default clone; 730 | 731 | function clone(item) { 732 | if (!item) { 733 | return item; 734 | } 735 | const type = typeof item; 736 | const string = Object.prototype.toString.call(item); 737 | 738 | let result = item; 739 | 740 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 741 | if (string === '[object Date]') { 742 | result = new Date(item); 743 | } else { 744 | result = {}; 745 | for (const i in item) { 746 | result[i] = clone(item[i]); 747 | } 748 | } 749 | } 750 | 751 | return result; 752 | }" 753 | `; 754 | 755 | exports[`clone(null) && clone({"name":"Luke"}) 1`] = ` 756 | "export default clone; 757 | 758 | function clone(item) { 759 | if (!item) { 760 | return item; 761 | } 762 | const type = typeof item; 763 | Object.prototype.toString.call(item); 764 | 765 | let result = item; 766 | 767 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 768 | result = {}; 769 | for (const i in item) { 770 | result[i] = clone(item[i]); 771 | } 772 | } 773 | 774 | return result; 775 | }" 776 | `; 777 | 778 | exports[`clone(null) && clone({"title":"How to Train Your Dragon","releaseDate":"2010-03-26T06:00:00.000Z"}) 1`] = ` 779 | "export default clone; 780 | 781 | function clone(item) { 782 | if (!item) { 783 | return item; 784 | } 785 | const type = typeof item; 786 | const string = Object.prototype.toString.call(item); 787 | 788 | let result = item; 789 | 790 | if (!(type !== \\"object\\" && type !== \\"function\\")) { 791 | if (string === '[object Date]') { 792 | result = new Date(item); 793 | } else { 794 | result = {}; 795 | for (const i in item) { 796 | result[i] = clone(item[i]); 797 | } 798 | } 799 | } 800 | 801 | return result; 802 | }" 803 | `; 804 | 805 | exports[`clone(null) 1`] = ` 806 | "export default clone; 807 | 808 | function clone(item) { 809 | return item; 810 | }" 811 | `; 812 | -------------------------------------------------------------------------------- /src/slice-code/test/__snapshots__/complex-fns.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`get("Han") 1`] = ` 4 | "export { unusedReturnAssign, get }; 5 | 6 | function unusedReturnAssign() { 7 | const foo = {}; 8 | function bar(msg) { 9 | return function () { 10 | return \`bar: \${msg}\`; 11 | }; 12 | } 13 | foo.getLuke = bar('luke'); 14 | foo.getLuke.characterName = 'luke'; 15 | 16 | foo.getHan = bar('han'); 17 | foo.getHan.characterName = 'han'; 18 | 19 | return foo; 20 | } 21 | 22 | function get(name) { 23 | return unusedReturnAssign()[\`get\${name}\`](); 24 | }" 25 | `; 26 | 27 | exports[`get("Luke") && get("Han") 1`] = ` 28 | "export { unusedReturnAssign, get }; 29 | 30 | function unusedReturnAssign() { 31 | const foo = {}; 32 | function bar(msg) { 33 | return function () { 34 | return \`bar: \${msg}\`; 35 | }; 36 | } 37 | foo.getLuke = bar('luke'); 38 | foo.getLuke.characterName = 'luke'; 39 | 40 | foo.getHan = bar('han'); 41 | foo.getHan.characterName = 'han'; 42 | 43 | return foo; 44 | } 45 | 46 | function get(name) { 47 | return unusedReturnAssign()[\`get\${name}\`](); 48 | }" 49 | `; 50 | 51 | exports[`get("Luke") 1`] = ` 52 | "export { unusedReturnAssign, get }; 53 | 54 | function unusedReturnAssign() { 55 | const foo = {}; 56 | function bar(msg) { 57 | return function () { 58 | return \`bar: \${msg}\`; 59 | }; 60 | } 61 | foo.getLuke = bar('luke'); 62 | foo.getLuke.characterName = 'luke'; 63 | 64 | foo.getHan = bar('han'); 65 | foo.getHan.characterName = 'han'; 66 | 67 | return foo; 68 | } 69 | 70 | function get(name) { 71 | return unusedReturnAssign()[\`get\${name}\`](); 72 | }" 73 | `; 74 | -------------------------------------------------------------------------------- /src/slice-code/test/__snapshots__/cond-expr.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`isConsequentOrAlternate(false) && isConsequentOrAlternate(true) 1`] = ` 4 | "export { isConsequentOrAlternate }; 5 | 6 | function isConsequentOrAlternate(isConsequent) { 7 | return isConsequent ? isConsequent : !isConsequent; 8 | }" 9 | `; 10 | 11 | exports[`isConsequentOrAlternate(false) 1`] = ` 12 | "export { isConsequentOrAlternate }; 13 | 14 | function isConsequentOrAlternate(isConsequent) { 15 | return !isConsequent; 16 | }" 17 | `; 18 | 19 | exports[`isConsequentOrAlternate(true) 1`] = ` 20 | "export { isConsequentOrAlternate }; 21 | 22 | function isConsequentOrAlternate(isConsequent) { 23 | return isConsequent; 24 | }" 25 | `; 26 | 27 | exports[`isConsequentOrAlternatesConsequentOrAlternate(false, false) && isConsequentOrAlternatesConsequentOrAlternate(false, true) && isConsequentOrAlternatesConsequentOrAlternate(true, false) && isConsequentOrAlternatesConsequentOrAlternate(true, true) 1`] = ` 28 | "export { isConsequentOrAlternatesConsequentOrAlternate }; 29 | 30 | function isConsequentOrAlternatesConsequentOrAlternate(isConsequent, isAlternatesConsequent) { 31 | return isConsequent ? isConsequent : isAlternatesConsequent ? isAlternatesConsequent : !isConsequent && !isAlternatesConsequent; 32 | }" 33 | `; 34 | 35 | exports[`isConsequentOrAlternatesConsequentOrAlternate(false, false) && isConsequentOrAlternatesConsequentOrAlternate(false, true) && isConsequentOrAlternatesConsequentOrAlternate(true, false) 1`] = ` 36 | "export { isConsequentOrAlternatesConsequentOrAlternate }; 37 | 38 | function isConsequentOrAlternatesConsequentOrAlternate(isConsequent, isAlternatesConsequent) { 39 | return isConsequent ? isConsequent : isAlternatesConsequent ? isAlternatesConsequent : !isConsequent && !isAlternatesConsequent; 40 | }" 41 | `; 42 | 43 | exports[`isConsequentOrAlternatesConsequentOrAlternate(false, false) && isConsequentOrAlternatesConsequentOrAlternate(false, true) && isConsequentOrAlternatesConsequentOrAlternate(true, true) 1`] = ` 44 | "export { isConsequentOrAlternatesConsequentOrAlternate }; 45 | 46 | function isConsequentOrAlternatesConsequentOrAlternate(isConsequent, isAlternatesConsequent) { 47 | return isConsequent ? isConsequent : isAlternatesConsequent ? isAlternatesConsequent : !isConsequent && !isAlternatesConsequent; 48 | }" 49 | `; 50 | 51 | exports[`isConsequentOrAlternatesConsequentOrAlternate(false, false) && isConsequentOrAlternatesConsequentOrAlternate(false, true) 1`] = ` 52 | "export { isConsequentOrAlternatesConsequentOrAlternate }; 53 | 54 | function isConsequentOrAlternatesConsequentOrAlternate(isConsequent, isAlternatesConsequent) { 55 | return isAlternatesConsequent ? isAlternatesConsequent : !isConsequent && !isAlternatesConsequent; 56 | }" 57 | `; 58 | 59 | exports[`isConsequentOrAlternatesConsequentOrAlternate(false, false) && isConsequentOrAlternatesConsequentOrAlternate(true, false) && isConsequentOrAlternatesConsequentOrAlternate(true, true) 1`] = ` 60 | "export { isConsequentOrAlternatesConsequentOrAlternate }; 61 | 62 | function isConsequentOrAlternatesConsequentOrAlternate(isConsequent, isAlternatesConsequent) { 63 | return isConsequent ? isConsequent : !isConsequent && !isAlternatesConsequent; 64 | }" 65 | `; 66 | 67 | exports[`isConsequentOrAlternatesConsequentOrAlternate(false, false) && isConsequentOrAlternatesConsequentOrAlternate(true, false) 1`] = ` 68 | "export { isConsequentOrAlternatesConsequentOrAlternate }; 69 | 70 | function isConsequentOrAlternatesConsequentOrAlternate(isConsequent, isAlternatesConsequent) { 71 | return isConsequent ? isConsequent : !isConsequent && !isAlternatesConsequent; 72 | }" 73 | `; 74 | 75 | exports[`isConsequentOrAlternatesConsequentOrAlternate(false, false) && isConsequentOrAlternatesConsequentOrAlternate(true, true) 1`] = ` 76 | "export { isConsequentOrAlternatesConsequentOrAlternate }; 77 | 78 | function isConsequentOrAlternatesConsequentOrAlternate(isConsequent, isAlternatesConsequent) { 79 | return isConsequent ? isConsequent : !isConsequent && !isAlternatesConsequent; 80 | }" 81 | `; 82 | 83 | exports[`isConsequentOrAlternatesConsequentOrAlternate(false, false) 1`] = ` 84 | "export { isConsequentOrAlternatesConsequentOrAlternate }; 85 | 86 | function isConsequentOrAlternatesConsequentOrAlternate(isConsequent, isAlternatesConsequent) { 87 | return !isConsequent && !isAlternatesConsequent; 88 | }" 89 | `; 90 | 91 | exports[`isConsequentOrAlternatesConsequentOrAlternate(false, true) && isConsequentOrAlternatesConsequentOrAlternate(true, false) && isConsequentOrAlternatesConsequentOrAlternate(true, true) 1`] = ` 92 | "export { isConsequentOrAlternatesConsequentOrAlternate }; 93 | 94 | function isConsequentOrAlternatesConsequentOrAlternate(isConsequent, isAlternatesConsequent) { 95 | return isConsequent ? isConsequent : isAlternatesConsequent; 96 | }" 97 | `; 98 | 99 | exports[`isConsequentOrAlternatesConsequentOrAlternate(false, true) && isConsequentOrAlternatesConsequentOrAlternate(true, false) 1`] = ` 100 | "export { isConsequentOrAlternatesConsequentOrAlternate }; 101 | 102 | function isConsequentOrAlternatesConsequentOrAlternate(isConsequent, isAlternatesConsequent) { 103 | return isConsequent ? isConsequent : isAlternatesConsequent; 104 | }" 105 | `; 106 | 107 | exports[`isConsequentOrAlternatesConsequentOrAlternate(false, true) && isConsequentOrAlternatesConsequentOrAlternate(true, true) 1`] = ` 108 | "export { isConsequentOrAlternatesConsequentOrAlternate }; 109 | 110 | function isConsequentOrAlternatesConsequentOrAlternate(isConsequent, isAlternatesConsequent) { 111 | return isConsequent ? isConsequent : isAlternatesConsequent; 112 | }" 113 | `; 114 | 115 | exports[`isConsequentOrAlternatesConsequentOrAlternate(false, true) 1`] = ` 116 | "export { isConsequentOrAlternatesConsequentOrAlternate }; 117 | 118 | function isConsequentOrAlternatesConsequentOrAlternate(isConsequent, isAlternatesConsequent) { 119 | return isAlternatesConsequent; 120 | }" 121 | `; 122 | 123 | exports[`isConsequentOrAlternatesConsequentOrAlternate(true, false) && isConsequentOrAlternatesConsequentOrAlternate(true, true) 1`] = ` 124 | "export { isConsequentOrAlternatesConsequentOrAlternate }; 125 | 126 | function isConsequentOrAlternatesConsequentOrAlternate(isConsequent) { 127 | return isConsequent; 128 | }" 129 | `; 130 | 131 | exports[`isConsequentOrAlternatesConsequentOrAlternate(true, false) 1`] = ` 132 | "export { isConsequentOrAlternatesConsequentOrAlternate }; 133 | 134 | function isConsequentOrAlternatesConsequentOrAlternate(isConsequent) { 135 | return isConsequent; 136 | }" 137 | `; 138 | 139 | exports[`isConsequentOrAlternatesConsequentOrAlternate(true, true) 1`] = ` 140 | "export { isConsequentOrAlternatesConsequentOrAlternate }; 141 | 142 | function isConsequentOrAlternatesConsequentOrAlternate(isConsequent) { 143 | return isConsequent; 144 | }" 145 | `; 146 | 147 | exports[`uncoveredConditionalExpression(false) && uncoveredConditionalExpression(true) 1`] = ` 148 | "export { uncoveredConditionalExpression }; 149 | 150 | function uncoveredConditionalExpression(cover) { 151 | if (cover) { 152 | return cover; 153 | } 154 | return !cover; 155 | }" 156 | `; 157 | 158 | exports[`uncoveredConditionalExpression(false) 1`] = ` 159 | "export { uncoveredConditionalExpression }; 160 | 161 | function uncoveredConditionalExpression(cover) { 162 | return !cover; 163 | }" 164 | `; 165 | 166 | exports[`uncoveredConditionalExpression(true) 1`] = ` 167 | "export { uncoveredConditionalExpression }; 168 | 169 | function uncoveredConditionalExpression(cover) { 170 | return cover; 171 | }" 172 | `; 173 | -------------------------------------------------------------------------------- /src/slice-code/test/__snapshots__/early-exit.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`condExpr(false) && condExpr(true) 1`] = ` 4 | "export { condExpr }; 5 | 6 | function condExpr(passIf) { 7 | if (!passIf) { 8 | return 'did not reach IfStatement'; 9 | } 10 | return 'reached CondExpr Consequent'; 11 | }" 12 | `; 13 | 14 | exports[`condExpr(false) 1`] = ` 15 | "export { condExpr }; 16 | 17 | function condExpr() { 18 | return 'did not reach IfStatement'; 19 | }" 20 | `; 21 | 22 | exports[`condExpr(true) 1`] = ` 23 | "export { condExpr }; 24 | 25 | function condExpr() { 26 | return 'reached CondExpr Consequent'; 27 | }" 28 | `; 29 | 30 | exports[`ifStatement(false) && ifStatement(true) 1`] = ` 31 | "export { ifStatement }; 32 | 33 | function ifStatement(passIf) { 34 | if (!passIf) { 35 | return 'did not reach IfStatement'; 36 | } 37 | 38 | return 'reached IfStatement'; 39 | }" 40 | `; 41 | 42 | exports[`ifStatement(false) 1`] = ` 43 | "export { ifStatement }; 44 | 45 | function ifStatement() { 46 | return 'did not reach IfStatement'; 47 | }" 48 | `; 49 | 50 | exports[`ifStatement(true) 1`] = ` 51 | "export { ifStatement }; 52 | 53 | function ifStatement() { 54 | return 'reached IfStatement'; 55 | }" 56 | `; 57 | 58 | exports[`logicalExpr(false) && logicalExpr(true) 1`] = ` 59 | "export { logicalExpr }; 60 | 61 | function logicalExpr(passIf) { 62 | if (!passIf) { 63 | return 'did not reach IfStatement'; 64 | } 65 | return passIf && 'reached LogicalExpression'; 66 | }" 67 | `; 68 | 69 | exports[`logicalExpr(false) 1`] = ` 70 | "export { logicalExpr }; 71 | 72 | function logicalExpr(passIf) { 73 | return 'did not reach IfStatement'; 74 | }" 75 | `; 76 | 77 | exports[`logicalExpr(true) 1`] = ` 78 | "export { logicalExpr }; 79 | 80 | function logicalExpr(passIf) { 81 | return passIf && 'reached LogicalExpression'; 82 | }" 83 | `; 84 | -------------------------------------------------------------------------------- /src/slice-code/test/__snapshots__/functions.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`divide(1, 2) 1`] = ` 4 | "export { divide }; 5 | 6 | function divide(a, b) { 7 | return a / b; 8 | }" 9 | `; 10 | 11 | exports[`divide(1, 2) 2`] = ` 12 | "export { divide }; 13 | 14 | function divide(a, b) { 15 | return a / b; 16 | }" 17 | `; 18 | 19 | exports[`multiply(1, 2) && divide(1, 2) 1`] = ` 20 | "export { sum, multiply, divide }; 21 | 22 | function sum(a, b) { 23 | return a + b; 24 | } 25 | 26 | function multiply(a, b) { 27 | let product, i; 28 | product = 0; 29 | for (i = 0; i < b; i++) { 30 | product = sum(product, a); 31 | } 32 | return product; 33 | } 34 | 35 | function divide(a, b) { 36 | return a / b; 37 | }" 38 | `; 39 | 40 | exports[`multiply(1, 2) 1`] = ` 41 | "export { sum, multiply }; 42 | 43 | function sum(a, b) { 44 | return a + b; 45 | } 46 | 47 | function multiply(a, b) { 48 | let product, i; 49 | product = 0; 50 | for (i = 0; i < b; i++) { 51 | product = sum(product, a); 52 | } 53 | return product; 54 | }" 55 | `; 56 | 57 | exports[`multiply(1, 2) 2`] = ` 58 | "export { sum, multiply }; 59 | 60 | function sum(a, b) { 61 | return a + b; 62 | } 63 | 64 | function multiply(a, b) { 65 | let product, i; 66 | product = 0; 67 | for (i = 0; i < b; i++) { 68 | product = sum(product, a); 69 | } 70 | return product; 71 | }" 72 | `; 73 | 74 | exports[`subtract(1, 2) && divide(1, 2) 1`] = ` 75 | "export { sum, subtract, divide }; 76 | 77 | function sum(a, b) { 78 | return a + b; 79 | } 80 | 81 | function subtract(a, b) { 82 | return sum(a, -b); 83 | } 84 | 85 | function divide(a, b) { 86 | return a / b; 87 | }" 88 | `; 89 | 90 | exports[`subtract(1, 2) && multiply(1, 2) && divide(1, 2) 1`] = ` 91 | "export { sum, subtract, multiply, divide }; 92 | 93 | function sum(a, b) { 94 | return a + b; 95 | } 96 | 97 | function subtract(a, b) { 98 | return sum(a, -b); 99 | } 100 | 101 | function multiply(a, b) { 102 | let product, i; 103 | product = 0; 104 | for (i = 0; i < b; i++) { 105 | product = sum(product, a); 106 | } 107 | return product; 108 | } 109 | 110 | function divide(a, b) { 111 | return a / b; 112 | }" 113 | `; 114 | 115 | exports[`subtract(1, 2) && multiply(1, 2) 1`] = ` 116 | "export { sum, subtract, multiply }; 117 | 118 | function sum(a, b) { 119 | return a + b; 120 | } 121 | 122 | function subtract(a, b) { 123 | return sum(a, -b); 124 | } 125 | 126 | function multiply(a, b) { 127 | let product, i; 128 | product = 0; 129 | for (i = 0; i < b; i++) { 130 | product = sum(product, a); 131 | } 132 | return product; 133 | }" 134 | `; 135 | 136 | exports[`subtract(1, 2) 1`] = ` 137 | "export { sum, subtract }; 138 | 139 | function sum(a, b) { 140 | return a + b; 141 | } 142 | 143 | function subtract(a, b) { 144 | return sum(a, -b); 145 | }" 146 | `; 147 | 148 | exports[`subtract(1, 2) 2`] = ` 149 | "export { sum, subtract }; 150 | 151 | function sum(a, b) { 152 | return a + b; 153 | } 154 | 155 | function subtract(a, b) { 156 | return sum(a, -b); 157 | }" 158 | `; 159 | 160 | exports[`sum(1, 2) && divide(1, 2) 1`] = ` 161 | "export { sum, divide }; 162 | 163 | function sum(a, b) { 164 | return a + b; 165 | } 166 | 167 | function divide(a, b) { 168 | return a / b; 169 | }" 170 | `; 171 | 172 | exports[`sum(1, 2) && multiply(1, 2) && divide(1, 2) 1`] = ` 173 | "export { sum, multiply, divide }; 174 | 175 | function sum(a, b) { 176 | return a + b; 177 | } 178 | 179 | function multiply(a, b) { 180 | let product, i; 181 | product = 0; 182 | for (i = 0; i < b; i++) { 183 | product = sum(product, a); 184 | } 185 | return product; 186 | } 187 | 188 | function divide(a, b) { 189 | return a / b; 190 | }" 191 | `; 192 | 193 | exports[`sum(1, 2) && multiply(1, 2) 1`] = ` 194 | "export { sum, multiply }; 195 | 196 | function sum(a, b) { 197 | return a + b; 198 | } 199 | 200 | function multiply(a, b) { 201 | let product, i; 202 | product = 0; 203 | for (i = 0; i < b; i++) { 204 | product = sum(product, a); 205 | } 206 | return product; 207 | }" 208 | `; 209 | 210 | exports[`sum(1, 2) && subtract(1, 2) && divide(1, 2) 1`] = ` 211 | "export { sum, subtract, divide }; 212 | 213 | function sum(a, b) { 214 | return a + b; 215 | } 216 | 217 | function subtract(a, b) { 218 | return sum(a, -b); 219 | } 220 | 221 | function divide(a, b) { 222 | return a / b; 223 | }" 224 | `; 225 | 226 | exports[`sum(1, 2) && subtract(1, 2) && multiply(1, 2) && divide(1, 2) 1`] = ` 227 | "export { sum, subtract, multiply, divide }; 228 | 229 | function sum(a, b) { 230 | return a + b; 231 | } 232 | 233 | function subtract(a, b) { 234 | return sum(a, -b); 235 | } 236 | 237 | function multiply(a, b) { 238 | let product, i; 239 | product = 0; 240 | for (i = 0; i < b; i++) { 241 | product = sum(product, a); 242 | } 243 | return product; 244 | } 245 | 246 | function divide(a, b) { 247 | return a / b; 248 | }" 249 | `; 250 | 251 | exports[`sum(1, 2) && subtract(1, 2) && multiply(1, 2) 1`] = ` 252 | "export { sum, subtract, multiply }; 253 | 254 | function sum(a, b) { 255 | return a + b; 256 | } 257 | 258 | function subtract(a, b) { 259 | return sum(a, -b); 260 | } 261 | 262 | function multiply(a, b) { 263 | let product, i; 264 | product = 0; 265 | for (i = 0; i < b; i++) { 266 | product = sum(product, a); 267 | } 268 | return product; 269 | }" 270 | `; 271 | 272 | exports[`sum(1, 2) && subtract(1, 2) 1`] = ` 273 | "export { sum, subtract }; 274 | 275 | function sum(a, b) { 276 | return a + b; 277 | } 278 | 279 | function subtract(a, b) { 280 | return sum(a, -b); 281 | }" 282 | `; 283 | 284 | exports[`sum(1, 2) 1`] = ` 285 | "export { sum }; 286 | 287 | function sum(a, b) { 288 | return a + b; 289 | }" 290 | `; 291 | 292 | exports[`sum(1, 2) 2`] = ` 293 | "export { sum }; 294 | 295 | function sum(a, b) { 296 | return a + b; 297 | }" 298 | `; 299 | -------------------------------------------------------------------------------- /src/slice-code/test/__snapshots__/if-statement-side-effects.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`ifWithAssignment(false) && ifWithAssignment(true) 1`] = ` 4 | "export { ifWithAssignment }; 5 | 6 | function ifWithAssignment(passIf) { 7 | let x; 8 | if ((x = 'hi') && passIf) { 9 | return x + passIf; 10 | } else { 11 | return !passIf + x; 12 | } 13 | }" 14 | `; 15 | 16 | exports[`ifWithAssignment(false) 1`] = ` 17 | "export { ifWithAssignment }; 18 | 19 | function ifWithAssignment(passIf) { 20 | let x = 'hi'; 21 | 22 | return !passIf + x; 23 | }" 24 | `; 25 | 26 | exports[`ifWithAssignment(true) 1`] = ` 27 | "export { ifWithAssignment }; 28 | 29 | function ifWithAssignment(passIf) { 30 | let x = 'hi'; 31 | 32 | return x + passIf; 33 | }" 34 | `; 35 | 36 | exports[`ifWithFunctionCall(false) && ifWithFunctionCall(true) 1`] = ` 37 | "export { ifWithFunctionCall }; 38 | 39 | function ifWithFunctionCall(passIf) { 40 | let thing; 41 | 42 | if (passIf && (a => (thing = a, passIf))('hey') && function (a) { 43 | return a; 44 | }(passIf)) { 45 | return thing + passIf; 46 | } else { 47 | return !passIf; 48 | } 49 | }" 50 | `; 51 | 52 | exports[`ifWithFunctionCall(false) 1`] = ` 53 | "export { ifWithFunctionCall }; 54 | 55 | function ifWithFunctionCall(passIf) { 56 | !function (a) { 57 | return a; 58 | }(passIf); 59 | 60 | return !passIf; 61 | }" 62 | `; 63 | 64 | exports[`ifWithFunctionCall(true) 1`] = ` 65 | "export { ifWithFunctionCall }; 66 | 67 | function ifWithFunctionCall(passIf) { 68 | let thing; 69 | 70 | passIf && (a => (thing = a, passIf))('hey'); 71 | (function (a) { 72 | return a; 73 | })(passIf); 74 | 75 | return thing + passIf; 76 | }" 77 | `; 78 | -------------------------------------------------------------------------------- /src/slice-code/test/__snapshots__/if-statements.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`ifElse(false) && ifElse(true) 1`] = ` 4 | "export { ifElse }; 5 | 6 | function ifElse(passIf) { 7 | if (passIf) { 8 | return passIf; 9 | } else { 10 | return !passIf; 11 | } 12 | }" 13 | `; 14 | 15 | exports[`ifElse(false) 1`] = ` 16 | "export { ifElse }; 17 | 18 | function ifElse(passIf) { 19 | return !passIf; 20 | }" 21 | `; 22 | 23 | exports[`ifElse(true) 1`] = ` 24 | "export { ifElse }; 25 | 26 | function ifElse(passIf) { 27 | return passIf; 28 | }" 29 | `; 30 | 31 | exports[`ifElseIfElse(false, false) && ifElseIfElse(false, true) && ifElseIfElse(true, false) && ifElseIfElse(true, true) 1`] = ` 32 | "export { ifElseIfElse }; 33 | 34 | function ifElseIfElse(passIf1, passIf2) { 35 | if (passIf1) { 36 | return passIf1; 37 | } else if (passIf2) { 38 | return passIf2; 39 | } else { 40 | return !passIf1 && !passIf2; 41 | } 42 | }" 43 | `; 44 | 45 | exports[`ifElseIfElse(false, false) && ifElseIfElse(false, true) && ifElseIfElse(true, false) 1`] = ` 46 | "export { ifElseIfElse }; 47 | 48 | function ifElseIfElse(passIf1, passIf2) { 49 | if (passIf1) { 50 | return passIf1; 51 | } else if (passIf2) { 52 | return passIf2; 53 | } else { 54 | return !passIf1 && !passIf2; 55 | } 56 | }" 57 | `; 58 | 59 | exports[`ifElseIfElse(false, false) && ifElseIfElse(false, true) && ifElseIfElse(true, true) 1`] = ` 60 | "export { ifElseIfElse }; 61 | 62 | function ifElseIfElse(passIf1, passIf2) { 63 | if (passIf1) { 64 | return passIf1; 65 | } else if (passIf2) { 66 | return passIf2; 67 | } else { 68 | return !passIf1 && !passIf2; 69 | } 70 | }" 71 | `; 72 | 73 | exports[`ifElseIfElse(false, false) && ifElseIfElse(false, true) 1`] = ` 74 | "export { ifElseIfElse }; 75 | 76 | function ifElseIfElse(passIf1, passIf2) { 77 | if (passIf2) { 78 | return passIf2; 79 | } else { 80 | return !passIf1 && !passIf2; 81 | } 82 | }" 83 | `; 84 | 85 | exports[`ifElseIfElse(false, false) && ifElseIfElse(true, false) && ifElseIfElse(true, true) 1`] = ` 86 | "export { ifElseIfElse }; 87 | 88 | function ifElseIfElse(passIf1, passIf2) { 89 | if (passIf1) { 90 | return passIf1; 91 | } else { 92 | return !passIf1 && !passIf2; 93 | } 94 | }" 95 | `; 96 | 97 | exports[`ifElseIfElse(false, false) && ifElseIfElse(true, false) 1`] = ` 98 | "export { ifElseIfElse }; 99 | 100 | function ifElseIfElse(passIf1, passIf2) { 101 | if (passIf1) { 102 | return passIf1; 103 | } else { 104 | return !passIf1 && !passIf2; 105 | } 106 | }" 107 | `; 108 | 109 | exports[`ifElseIfElse(false, false) && ifElseIfElse(true, true) 1`] = ` 110 | "export { ifElseIfElse }; 111 | 112 | function ifElseIfElse(passIf1, passIf2) { 113 | if (passIf1) { 114 | return passIf1; 115 | } else { 116 | return !passIf1 && !passIf2; 117 | } 118 | }" 119 | `; 120 | 121 | exports[`ifElseIfElse(false, false) 1`] = ` 122 | "export { ifElseIfElse }; 123 | 124 | function ifElseIfElse(passIf1, passIf2) { 125 | return !passIf1 && !passIf2; 126 | }" 127 | `; 128 | 129 | exports[`ifElseIfElse(false, true) && ifElseIfElse(true, false) && ifElseIfElse(true, true) 1`] = ` 130 | "export { ifElseIfElse }; 131 | 132 | function ifElseIfElse(passIf1, passIf2) { 133 | if (passIf1) { 134 | return passIf1; 135 | } else { 136 | return passIf2; 137 | } 138 | }" 139 | `; 140 | 141 | exports[`ifElseIfElse(false, true) && ifElseIfElse(true, false) 1`] = ` 142 | "export { ifElseIfElse }; 143 | 144 | function ifElseIfElse(passIf1, passIf2) { 145 | if (passIf1) { 146 | return passIf1; 147 | } else { 148 | return passIf2; 149 | } 150 | }" 151 | `; 152 | 153 | exports[`ifElseIfElse(false, true) && ifElseIfElse(true, true) 1`] = ` 154 | "export { ifElseIfElse }; 155 | 156 | function ifElseIfElse(passIf1, passIf2) { 157 | if (passIf1) { 158 | return passIf1; 159 | } else { 160 | return passIf2; 161 | } 162 | }" 163 | `; 164 | 165 | exports[`ifElseIfElse(false, true) 1`] = ` 166 | "export { ifElseIfElse }; 167 | 168 | function ifElseIfElse(passIf1, passIf2) { 169 | return passIf2; 170 | }" 171 | `; 172 | 173 | exports[`ifElseIfElse(true, false) && ifElseIfElse(true, true) 1`] = ` 174 | "export { ifElseIfElse }; 175 | 176 | function ifElseIfElse(passIf1) { 177 | return passIf1; 178 | }" 179 | `; 180 | 181 | exports[`ifElseIfElse(true, false) 1`] = ` 182 | "export { ifElseIfElse }; 183 | 184 | function ifElseIfElse(passIf1) { 185 | return passIf1; 186 | }" 187 | `; 188 | 189 | exports[`ifElseIfElse(true, true) 1`] = ` 190 | "export { ifElseIfElse }; 191 | 192 | function ifElseIfElse(passIf1) { 193 | return passIf1; 194 | }" 195 | `; 196 | 197 | exports[`ifOnly(false) && ifOnly(true) 1`] = ` 198 | "export { ifOnly }; 199 | 200 | function ifOnly(passIf) { 201 | if (passIf) { 202 | return passIf; 203 | } 204 | }" 205 | `; 206 | 207 | exports[`ifOnly(false) 1`] = ` 208 | "export { ifOnly }; 209 | 210 | function ifOnly() {}" 211 | `; 212 | 213 | exports[`ifOnly(true) 1`] = ` 214 | "export { ifOnly }; 215 | 216 | function ifOnly(passIf) { 217 | return passIf; 218 | }" 219 | `; 220 | 221 | exports[`nestedIf(false, false) && nestedIf(false, true) && nestedIf(true, false) && nestedIf(true, true) 1`] = ` 222 | "export { nestedIf }; 223 | 224 | function nestedIf(passIf1, passIf2) { 225 | if (passIf1) { 226 | if (passIf2) { 227 | return passIf2; 228 | } 229 | return passIf1; 230 | } 231 | }" 232 | `; 233 | 234 | exports[`nestedIf(false, false) && nestedIf(false, true) && nestedIf(true, false) 1`] = ` 235 | "export { nestedIf }; 236 | 237 | function nestedIf(passIf1) { 238 | if (passIf1) { 239 | return passIf1; 240 | } 241 | }" 242 | `; 243 | 244 | exports[`nestedIf(false, false) && nestedIf(false, true) && nestedIf(true, true) 1`] = ` 245 | "export { nestedIf }; 246 | 247 | function nestedIf(passIf1, passIf2) { 248 | if (passIf1) { 249 | return passIf2; 250 | } 251 | }" 252 | `; 253 | 254 | exports[`nestedIf(false, false) && nestedIf(false, true) 1`] = ` 255 | "export { nestedIf }; 256 | 257 | function nestedIf() {}" 258 | `; 259 | 260 | exports[`nestedIf(false, false) && nestedIf(true, false) && nestedIf(true, true) 1`] = ` 261 | "export { nestedIf }; 262 | 263 | function nestedIf(passIf1, passIf2) { 264 | if (passIf1) { 265 | if (passIf2) { 266 | return passIf2; 267 | } 268 | return passIf1; 269 | } 270 | }" 271 | `; 272 | 273 | exports[`nestedIf(false, false) && nestedIf(true, false) 1`] = ` 274 | "export { nestedIf }; 275 | 276 | function nestedIf(passIf1) { 277 | if (passIf1) { 278 | return passIf1; 279 | } 280 | }" 281 | `; 282 | 283 | exports[`nestedIf(false, false) && nestedIf(true, true) 1`] = ` 284 | "export { nestedIf }; 285 | 286 | function nestedIf(passIf1, passIf2) { 287 | if (passIf1) { 288 | return passIf2; 289 | } 290 | }" 291 | `; 292 | 293 | exports[`nestedIf(false, false) 1`] = ` 294 | "export { nestedIf }; 295 | 296 | function nestedIf() {}" 297 | `; 298 | 299 | exports[`nestedIf(false, true) && nestedIf(true, false) && nestedIf(true, true) 1`] = ` 300 | "export { nestedIf }; 301 | 302 | function nestedIf(passIf1, passIf2) { 303 | if (passIf1) { 304 | if (passIf2) { 305 | return passIf2; 306 | } 307 | return passIf1; 308 | } 309 | }" 310 | `; 311 | 312 | exports[`nestedIf(false, true) && nestedIf(true, false) 1`] = ` 313 | "export { nestedIf }; 314 | 315 | function nestedIf(passIf1) { 316 | if (passIf1) { 317 | return passIf1; 318 | } 319 | }" 320 | `; 321 | 322 | exports[`nestedIf(false, true) && nestedIf(true, true) 1`] = ` 323 | "export { nestedIf }; 324 | 325 | function nestedIf(passIf1, passIf2) { 326 | if (passIf1) { 327 | return passIf2; 328 | } 329 | }" 330 | `; 331 | 332 | exports[`nestedIf(false, true) 1`] = ` 333 | "export { nestedIf }; 334 | 335 | function nestedIf() {}" 336 | `; 337 | 338 | exports[`nestedIf(true, false) && nestedIf(true, true) 1`] = ` 339 | "export { nestedIf }; 340 | 341 | function nestedIf(passIf1, passIf2) { 342 | if (passIf2) { 343 | return passIf2; 344 | } 345 | return passIf1; 346 | }" 347 | `; 348 | 349 | exports[`nestedIf(true, false) 1`] = ` 350 | "export { nestedIf }; 351 | 352 | function nestedIf(passIf1) { 353 | return passIf1; 354 | }" 355 | `; 356 | 357 | exports[`nestedIf(true, true) 1`] = ` 358 | "export { nestedIf }; 359 | 360 | function nestedIf(passIf1, passIf2) { 361 | return passIf2; 362 | }" 363 | `; 364 | -------------------------------------------------------------------------------- /src/slice-code/test/__snapshots__/indent-string.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`indentString("hello world") && indentString("hello world", , "@") && indentString("hello world", 4, "@") 1`] = ` 4 | "'use strict'; 5 | 6 | module.exports = (str, count, indent) => { 7 | indent = indent === undefined ? ' ' : indent; 8 | count = count === undefined ? 1 : count; 9 | 10 | return str.replace(/^(?!\\\\s*$)/mg, indent.repeat(count)); 11 | };" 12 | `; 13 | 14 | exports[`indentString("hello world") && indentString("hello world", , "@") 1`] = ` 15 | "'use strict'; 16 | 17 | module.exports = (str, count, indent) => { 18 | indent = indent === undefined ? ' ' : indent; 19 | count = 1; 20 | 21 | return str.replace(/^(?!\\\\s*$)/mg, indent.repeat(count)); 22 | };" 23 | `; 24 | 25 | exports[`indentString("hello world") && indentString("hello world", 2) && indentString("hello world", , "@") && indentString("hello world", 4, "@") 1`] = ` 26 | "'use strict'; 27 | 28 | module.exports = (str, count, indent) => { 29 | indent = indent === undefined ? ' ' : indent; 30 | count = count === undefined ? 1 : count; 31 | 32 | return str.replace(/^(?!\\\\s*$)/mg, indent.repeat(count)); 33 | };" 34 | `; 35 | 36 | exports[`indentString("hello world") && indentString("hello world", 2) && indentString("hello world", , "@") 1`] = ` 37 | "'use strict'; 38 | 39 | module.exports = (str, count, indent) => { 40 | indent = indent === undefined ? ' ' : indent; 41 | count = count === undefined ? 1 : count; 42 | 43 | return str.replace(/^(?!\\\\s*$)/mg, indent.repeat(count)); 44 | };" 45 | `; 46 | 47 | exports[`indentString("hello world") && indentString("hello world", 2) && indentString("hello world", 4, "@") 1`] = ` 48 | "'use strict'; 49 | 50 | module.exports = (str, count, indent) => { 51 | indent = indent === undefined ? ' ' : indent; 52 | count = count === undefined ? 1 : count; 53 | 54 | return str.replace(/^(?!\\\\s*$)/mg, indent.repeat(count)); 55 | };" 56 | `; 57 | 58 | exports[`indentString("hello world") && indentString("hello world", 2) 1`] = ` 59 | "'use strict'; 60 | 61 | module.exports = (str, count, indent) => { 62 | indent = ' '; 63 | count = count === undefined ? 1 : count; 64 | 65 | return str.replace(/^(?!\\\\s*$)/mg, indent.repeat(count)); 66 | };" 67 | `; 68 | 69 | exports[`indentString("hello world") && indentString("hello world", 4, "@") 1`] = ` 70 | "'use strict'; 71 | 72 | module.exports = (str, count, indent) => { 73 | indent = indent === undefined ? ' ' : indent; 74 | count = count === undefined ? 1 : count; 75 | 76 | return str.replace(/^(?!\\\\s*$)/mg, indent.repeat(count)); 77 | };" 78 | `; 79 | 80 | exports[`indentString("hello world") 1`] = ` 81 | "'use strict'; 82 | 83 | module.exports = (str, count, indent) => { 84 | indent = ' '; 85 | count = 1; 86 | 87 | return str.replace(/^(?!\\\\s*$)/mg, indent.repeat(count)); 88 | };" 89 | `; 90 | 91 | exports[`indentString("hello world", , "@") && indentString("hello world", 4, "@") 1`] = ` 92 | "'use strict'; 93 | 94 | module.exports = (str, count, indent) => { 95 | count = count === undefined ? 1 : count; 96 | 97 | return str.replace(/^(?!\\\\s*$)/mg, indent.repeat(count)); 98 | };" 99 | `; 100 | 101 | exports[`indentString("hello world", , "@") 1`] = ` 102 | "'use strict'; 103 | 104 | module.exports = (str, count, indent) => { 105 | count = 1; 106 | 107 | return str.replace(/^(?!\\\\s*$)/mg, indent.repeat(count)); 108 | };" 109 | `; 110 | 111 | exports[`indentString("hello world", 2) && indentString("hello world", , "@") && indentString("hello world", 4, "@") 1`] = ` 112 | "'use strict'; 113 | 114 | module.exports = (str, count, indent) => { 115 | indent = indent === undefined ? ' ' : indent; 116 | count = count === undefined ? 1 : count; 117 | 118 | return str.replace(/^(?!\\\\s*$)/mg, indent.repeat(count)); 119 | };" 120 | `; 121 | 122 | exports[`indentString("hello world", 2) && indentString("hello world", , "@") 1`] = ` 123 | "'use strict'; 124 | 125 | module.exports = (str, count, indent) => { 126 | indent = indent === undefined ? ' ' : indent; 127 | count = count === undefined ? 1 : count; 128 | 129 | return str.replace(/^(?!\\\\s*$)/mg, indent.repeat(count)); 130 | };" 131 | `; 132 | 133 | exports[`indentString("hello world", 2) && indentString("hello world", 4, "@") 1`] = ` 134 | "'use strict'; 135 | 136 | module.exports = (str, count, indent) => { 137 | indent = indent === undefined ? ' ' : indent; 138 | 139 | 140 | return str.replace(/^(?!\\\\s*$)/mg, indent.repeat(count)); 141 | };" 142 | `; 143 | 144 | exports[`indentString("hello world", 2) 1`] = ` 145 | "'use strict'; 146 | 147 | module.exports = (str, count, indent) => { 148 | indent = ' '; 149 | 150 | 151 | return str.replace(/^(?!\\\\s*$)/mg, indent.repeat(count)); 152 | };" 153 | `; 154 | 155 | exports[`indentString("hello world", 4, "@") 1`] = ` 156 | "'use strict'; 157 | 158 | module.exports = (str, count, indent) => { 159 | 160 | return str.replace(/^(?!\\\\s*$)/mg, indent.repeat(count)); 161 | };" 162 | `; 163 | -------------------------------------------------------------------------------- /src/slice-code/test/__snapshots__/logical-expressions.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`allAnd(false, false, false) 1`] = `"export const allAnd = a => a;"`; 4 | 5 | exports[`allAnd(false, false, true) 1`] = `"export const allAnd = a => a;"`; 6 | 7 | exports[`allAnd(false, true, false) 1`] = `"export const allAnd = a => a;"`; 8 | 9 | exports[`allAnd(false, true, true) 1`] = `"export const allAnd = a => a;"`; 10 | 11 | exports[`allAnd(true, false, false) 1`] = `"export const allAnd = (a, b) => a && b;"`; 12 | 13 | exports[`allAnd(true, false, true) 1`] = `"export const allAnd = (a, b) => a && b;"`; 14 | 15 | exports[`allAnd(true, true, false) 1`] = `"export const allAnd = (a, b, c) => a && b && c;"`; 16 | 17 | exports[`allAnd(true, true, true) 1`] = `"export const allAnd = (a, b, c) => a && b && c;"`; 18 | 19 | exports[`allOr(false, false, false) 1`] = ` 20 | " 21 | export const allOr = (a, b, c) => a || b || c;" 22 | `; 23 | 24 | exports[`allOr(false, false, true) 1`] = ` 25 | " 26 | export const allOr = (a, b, c) => a || b || c;" 27 | `; 28 | 29 | exports[`allOr(false, true, false) 1`] = ` 30 | " 31 | export const allOr = (a, b) => a || b;" 32 | `; 33 | 34 | exports[`allOr(false, true, true) 1`] = ` 35 | " 36 | export const allOr = (a, b) => a || b;" 37 | `; 38 | 39 | exports[`allOr(true, false, false) 1`] = ` 40 | " 41 | export const allOr = a => a;" 42 | `; 43 | 44 | exports[`allOr(true, false, true) 1`] = ` 45 | " 46 | export const allOr = a => a;" 47 | `; 48 | 49 | exports[`allOr(true, true, false) 1`] = ` 50 | " 51 | export const allOr = a => a;" 52 | `; 53 | 54 | exports[`allOr(true, true, true) 1`] = ` 55 | " 56 | export const allOr = a => a;" 57 | `; 58 | 59 | exports[`andOr(false, false, false) 1`] = ` 60 | " 61 | export const andOr = (a, b, c) => a || c;" 62 | `; 63 | 64 | exports[`andOr(false, false, true) 1`] = ` 65 | " 66 | export const andOr = (a, b, c) => a || c;" 67 | `; 68 | 69 | exports[`andOr(false, true, false) 1`] = ` 70 | " 71 | export const andOr = (a, b, c) => a || c;" 72 | `; 73 | 74 | exports[`andOr(false, true, true) 1`] = ` 75 | " 76 | export const andOr = (a, b, c) => a || c;" 77 | `; 78 | 79 | exports[`andOr(true, false, false) 1`] = ` 80 | " 81 | export const andOr = (a, b, c) => a && b || c;" 82 | `; 83 | 84 | exports[`andOr(true, false, true) 1`] = ` 85 | " 86 | export const andOr = (a, b, c) => a && b || c;" 87 | `; 88 | 89 | exports[`andOr(true, true, false) 1`] = ` 90 | " 91 | export const andOr = (a, b) => a && b;" 92 | `; 93 | 94 | exports[`andOr(true, true, true) 1`] = ` 95 | " 96 | export const andOr = (a, b) => a && b;" 97 | `; 98 | 99 | exports[`andOrAnd(false, false, false, false) 1`] = ` 100 | " 101 | export const andOrAnd = (a, b, c) => a || c;" 102 | `; 103 | 104 | exports[`andOrAnd(false, false, false, true) 1`] = ` 105 | " 106 | export const andOrAnd = (a, b, c) => a || c;" 107 | `; 108 | 109 | exports[`andOrAnd(false, false, true, false) 1`] = ` 110 | " 111 | export const andOrAnd = (a, b, c, d) => a || c && d;" 112 | `; 113 | 114 | exports[`andOrAnd(false, false, true, true) 1`] = ` 115 | " 116 | export const andOrAnd = (a, b, c, d) => a || c && d;" 117 | `; 118 | 119 | exports[`andOrAnd(false, true, false, false) 1`] = ` 120 | " 121 | export const andOrAnd = (a, b, c) => a || c;" 122 | `; 123 | 124 | exports[`andOrAnd(false, true, false, true) 1`] = ` 125 | " 126 | export const andOrAnd = (a, b, c) => a || c;" 127 | `; 128 | 129 | exports[`andOrAnd(false, true, true, false) 1`] = ` 130 | " 131 | export const andOrAnd = (a, b, c, d) => a || c && d;" 132 | `; 133 | 134 | exports[`andOrAnd(false, true, true, true) 1`] = ` 135 | " 136 | export const andOrAnd = (a, b, c, d) => a || c && d;" 137 | `; 138 | 139 | exports[`andOrAnd(true, false, false, false) 1`] = ` 140 | " 141 | export const andOrAnd = (a, b, c) => a && b || c;" 142 | `; 143 | 144 | exports[`andOrAnd(true, false, false, true) 1`] = ` 145 | " 146 | export const andOrAnd = (a, b, c) => a && b || c;" 147 | `; 148 | 149 | exports[`andOrAnd(true, false, true, false) 1`] = ` 150 | " 151 | export const andOrAnd = (a, b, c, d) => a && b || c && d;" 152 | `; 153 | 154 | exports[`andOrAnd(true, false, true, true) 1`] = ` 155 | " 156 | export const andOrAnd = (a, b, c, d) => a && b || c && d;" 157 | `; 158 | 159 | exports[`andOrAnd(true, true, false, false) 1`] = ` 160 | " 161 | export const andOrAnd = (a, b) => a && b;" 162 | `; 163 | 164 | exports[`andOrAnd(true, true, false, true) 1`] = ` 165 | " 166 | export const andOrAnd = (a, b) => a && b;" 167 | `; 168 | 169 | exports[`andOrAnd(true, true, true, false) 1`] = ` 170 | " 171 | export const andOrAnd = (a, b) => a && b;" 172 | `; 173 | 174 | exports[`andOrAnd(true, true, true, true) 1`] = ` 175 | " 176 | export const andOrAnd = (a, b) => a && b;" 177 | `; 178 | 179 | exports[`orAndOr(false, false, false, false) 1`] = ` 180 | " 181 | export const orAndOr = (a, b) => a || b;" 182 | `; 183 | 184 | exports[`orAndOr(false, false, false, true) 1`] = ` 185 | " 186 | export const orAndOr = (a, b) => a || b;" 187 | `; 188 | 189 | exports[`orAndOr(false, false, true, false) 1`] = ` 190 | " 191 | export const orAndOr = (a, b) => a || b;" 192 | `; 193 | 194 | exports[`orAndOr(false, false, true, true) 1`] = ` 195 | " 196 | export const orAndOr = (a, b) => a || b;" 197 | `; 198 | 199 | exports[`orAndOr(false, true, false, false) 1`] = ` 200 | " 201 | export const orAndOr = (a, b, c, d) => (a || b) && (c || d);" 202 | `; 203 | 204 | exports[`orAndOr(false, true, false, true) 1`] = ` 205 | " 206 | export const orAndOr = (a, b, c, d) => (a || b) && (c || d);" 207 | `; 208 | 209 | exports[`orAndOr(false, true, true, false) 1`] = ` 210 | " 211 | export const orAndOr = (a, b, c) => (a || b) && c;" 212 | `; 213 | 214 | exports[`orAndOr(false, true, true, true) 1`] = ` 215 | " 216 | export const orAndOr = (a, b, c) => (a || b) && c;" 217 | `; 218 | 219 | exports[`orAndOr(true, false, false, false) 1`] = ` 220 | " 221 | export const orAndOr = (a, b, c, d) => a && (c || d);" 222 | `; 223 | 224 | exports[`orAndOr(true, false, false, true) 1`] = ` 225 | " 226 | export const orAndOr = (a, b, c, d) => a && (c || d);" 227 | `; 228 | 229 | exports[`orAndOr(true, false, true, false) 1`] = ` 230 | " 231 | export const orAndOr = (a, b, c) => a && c;" 232 | `; 233 | 234 | exports[`orAndOr(true, false, true, true) 1`] = ` 235 | " 236 | export const orAndOr = (a, b, c) => a && c;" 237 | `; 238 | 239 | exports[`orAndOr(true, true, false, false) 1`] = ` 240 | " 241 | export const orAndOr = (a, b, c, d) => a && (c || d);" 242 | `; 243 | 244 | exports[`orAndOr(true, true, false, true) 1`] = ` 245 | " 246 | export const orAndOr = (a, b, c, d) => a && (c || d);" 247 | `; 248 | 249 | exports[`orAndOr(true, true, true, false) 1`] = ` 250 | " 251 | export const orAndOr = (a, b, c) => a && c;" 252 | `; 253 | 254 | exports[`orAndOr(true, true, true, true) 1`] = ` 255 | " 256 | export const orAndOr = (a, b, c) => a && c;" 257 | `; 258 | -------------------------------------------------------------------------------- /src/slice-code/test/__snapshots__/match-sorter.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`matchSorter(["hi","hey","hello","sup","yo"], "y") 1`] = ` 4 | "'use strict'; 5 | 6 | var _diacritic = require('diacritic'); 7 | 8 | var _diacritic2 = _interopRequireDefault(_diacritic); 9 | 10 | var _globalObject = require('global-object'); 11 | 12 | function _interopRequireDefault(obj) { 13 | return { default: obj }; 14 | } 15 | 16 | var rankings = { 17 | CASE_SENSITIVE_EQUAL: 7, 18 | EQUAL: 6, 19 | STARTS_WITH: 5, 20 | WORD_STARTS_WITH: 4, 21 | CONTAINS: 3, 22 | ACRONYM: 2, 23 | MATCHES: 1, 24 | NO_MATCH: 0 25 | }; 26 | 27 | matchSorter.rankings = rankings; 28 | 29 | function matchSorter(items, value) { 30 | var options = {}; 31 | var keys = options.keys, 32 | threshold = rankings.MATCHES; 33 | 34 | var matchedItems = items.reduce(function (matches, item, index) { 35 | var _getHighestRanking = getHighestRanking(item, keys, value, options), 36 | rank = _getHighestRanking.rank, 37 | keyIndex = _getHighestRanking.keyIndex; 38 | 39 | if (rank >= threshold) { 40 | matches.push({ item: item, rank: rank, index: index, keyIndex: keyIndex }); 41 | } 42 | return matches; 43 | }, []); 44 | return matchedItems.sort(sortRankedItems).map(function (_ref) { 45 | var item = _ref.item; 46 | return item; 47 | }); 48 | } 49 | 50 | function getHighestRanking(item, keys, value, options) { 51 | return { rank: getMatchRanking(item, value, options), keyIndex: -1 }; 52 | } 53 | 54 | function getMatchRanking(testString, stringToRank, options) { 55 | testString = prepareValueForComparison(testString, options); 56 | stringToRank = prepareValueForComparison(stringToRank, options); 57 | 58 | testString = testString.toLowerCase(); 59 | stringToRank = stringToRank.toLowerCase(); 60 | 61 | if (testString.indexOf(stringToRank) === 0) { 62 | return rankings.STARTS_WITH; 63 | } 64 | 65 | if (testString.indexOf(stringToRank) !== -1) { 66 | return rankings.CONTAINS; 67 | } else { 68 | return rankings.NO_MATCH; 69 | } 70 | } 71 | 72 | function sortRankedItems(a, b) { 73 | 74 | return 1; 75 | } 76 | 77 | function prepareValueForComparison(value, _ref4) { 78 | 79 | value = '' + value; 80 | value = _diacritic2.default.clean(value); 81 | 82 | return value; 83 | } 84 | 85 | matchSorter.default = matchSorter; 86 | module.exports = matchSorter; 87 | Object.defineProperty(exports, '__esModule', { value: true });" 88 | `; 89 | -------------------------------------------------------------------------------- /src/slice-code/test/__snapshots__/module-pattern.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`({ default: mod }) => [mod.baz(false)] 1`] = ` 4 | "export default (() => { 5 | const mod = {}; 6 | 7 | mod.bar = 'bar'; 8 | 9 | mod.baz = () => 'buzz'; 10 | 11 | mod.mokeypatchFn = /*slice-js-coverage-ignore ignore next*/function () {}; 12 | mod.mokeypatchFn.favorite = 'my favorite animal is a 🐒'; 13 | 14 | mod.monkeypatchArrow = /*slice-js-coverage-ignore ignore next*/() => {}; 15 | mod.monkeypatchArrow.favorite = 'my favorite animal is a 🐵'; 16 | 17 | return mod; 18 | })();" 19 | `; 20 | 21 | exports[`({ default: mod }) => [mod.baz(false)] 2`] = ` 22 | "export default (() => { 23 | const mod = {}; 24 | 25 | mod.bar = 'bar'; 26 | 27 | mod.baz = () => 'buzz'; 28 | 29 | mod.mokeypatchFn = /*slice-js-coverage-ignore ignore next*/function () {}; 30 | mod.mokeypatchFn.favorite = 'my favorite animal is a 🐒'; 31 | 32 | mod.monkeypatchArrow = /*slice-js-coverage-ignore ignore next*/() => {}; 33 | mod.monkeypatchArrow.favorite = 'my favorite animal is a 🐵'; 34 | 35 | return mod; 36 | })();" 37 | `; 38 | 39 | exports[`({ default: mod }) => [mod.baz(true)] 1`] = ` 40 | "export default (() => { 41 | const mod = {}; 42 | 43 | mod.bar = 'bar'; 44 | 45 | mod.baz = () => 'baz'; 46 | 47 | mod.mokeypatchFn = /*slice-js-coverage-ignore ignore next*/function () {}; 48 | mod.mokeypatchFn.favorite = 'my favorite animal is a 🐒'; 49 | 50 | mod.monkeypatchArrow = /*slice-js-coverage-ignore ignore next*/() => {}; 51 | mod.monkeypatchArrow.favorite = 'my favorite animal is a 🐵'; 52 | 53 | return mod; 54 | })();" 55 | `; 56 | 57 | exports[`({ default: mod }) => [mod.foo(), mod.baz(false)] 1`] = ` 58 | "export default (() => { 59 | const mod = {}; 60 | 61 | mod.foo = () => 'foo'; 62 | 63 | mod.bar = 'bar'; 64 | 65 | mod.baz = () => 'buzz'; 66 | 67 | mod.mokeypatchFn = /*slice-js-coverage-ignore ignore next*/function () {}; 68 | mod.mokeypatchFn.favorite = 'my favorite animal is a 🐒'; 69 | 70 | mod.monkeypatchArrow = /*slice-js-coverage-ignore ignore next*/() => {}; 71 | mod.monkeypatchArrow.favorite = 'my favorite animal is a 🐵'; 72 | 73 | return mod; 74 | })();" 75 | `; 76 | 77 | exports[`({ default: mod }) => [mod.foo(), mod.baz(true)] 1`] = ` 78 | "export default (() => { 79 | const mod = {}; 80 | 81 | mod.foo = () => 'foo'; 82 | 83 | mod.bar = 'bar'; 84 | 85 | mod.baz = () => 'baz'; 86 | 87 | mod.mokeypatchFn = /*slice-js-coverage-ignore ignore next*/function () {}; 88 | mod.mokeypatchFn.favorite = 'my favorite animal is a 🐒'; 89 | 90 | mod.monkeypatchArrow = /*slice-js-coverage-ignore ignore next*/() => {}; 91 | mod.monkeypatchArrow.favorite = 'my favorite animal is a 🐵'; 92 | 93 | return mod; 94 | })();" 95 | `; 96 | 97 | exports[`({ default: mod }) => [mod.foo()] 1`] = ` 98 | "export default (() => { 99 | const mod = {}; 100 | 101 | mod.foo = () => 'foo'; 102 | 103 | mod.bar = 'bar'; 104 | 105 | mod.mokeypatchFn = /*slice-js-coverage-ignore ignore next*/function () {}; 106 | mod.mokeypatchFn.favorite = 'my favorite animal is a 🐒'; 107 | 108 | mod.monkeypatchArrow = /*slice-js-coverage-ignore ignore next*/() => {}; 109 | mod.monkeypatchArrow.favorite = 'my favorite animal is a 🐵'; 110 | 111 | return mod; 112 | })();" 113 | `; 114 | 115 | exports[`({ default: mod }) => [mod.foobar(), mod.baz(false)] 1`] = ` 116 | "export default (() => { 117 | const mod = {}; 118 | 119 | mod.bar = 'bar'; 120 | 121 | mod.baz = () => 'buzz'; 122 | 123 | mod.foobar = function () { 124 | return 'function assignment'; 125 | }; 126 | 127 | mod.mokeypatchFn = /*slice-js-coverage-ignore ignore next*/function () {}; 128 | mod.mokeypatchFn.favorite = 'my favorite animal is a 🐒'; 129 | 130 | mod.monkeypatchArrow = /*slice-js-coverage-ignore ignore next*/() => {}; 131 | mod.monkeypatchArrow.favorite = 'my favorite animal is a 🐵'; 132 | 133 | return mod; 134 | })();" 135 | `; 136 | 137 | exports[`({ default: mod }) => [mod.foobar(), mod.foo()] 1`] = ` 138 | "export default (() => { 139 | const mod = {}; 140 | 141 | mod.foo = () => 'foo'; 142 | 143 | mod.bar = 'bar'; 144 | 145 | mod.foobar = function () { 146 | return 'function assignment'; 147 | }; 148 | 149 | mod.mokeypatchFn = /*slice-js-coverage-ignore ignore next*/function () {}; 150 | mod.mokeypatchFn.favorite = 'my favorite animal is a 🐒'; 151 | 152 | mod.monkeypatchArrow = /*slice-js-coverage-ignore ignore next*/() => {}; 153 | mod.monkeypatchArrow.favorite = 'my favorite animal is a 🐵'; 154 | 155 | return mod; 156 | })();" 157 | `; 158 | 159 | exports[`({ default: mod }) => [mod.foobar()] 1`] = ` 160 | "export default (() => { 161 | const mod = {}; 162 | 163 | mod.bar = 'bar'; 164 | 165 | mod.foobar = function () { 166 | return 'function assignment'; 167 | }; 168 | 169 | mod.mokeypatchFn = /*slice-js-coverage-ignore ignore next*/function () {}; 170 | mod.mokeypatchFn.favorite = 'my favorite animal is a 🐒'; 171 | 172 | mod.monkeypatchArrow = /*slice-js-coverage-ignore ignore next*/() => {}; 173 | mod.monkeypatchArrow.favorite = 'my favorite animal is a 🐵'; 174 | 175 | return mod; 176 | })();" 177 | `; 178 | -------------------------------------------------------------------------------- /src/slice-code/test/__snapshots__/pizza.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`makePizza({"type":"cheese","size":"Large"}) 1`] = ` 4 | "export { makePizza }; 5 | 6 | const getCrust = (size, crustType) => \`\${size}: \${crustType || 'Hand tossed pizza'}\`; 7 | 8 | function makePizza(order) { 9 | const pizza = { 10 | type: order.type, 11 | crust: getCrust(order.size, order.crustType), 12 | crustEdge: order.crustEdge || 'Garlic Buttery Blend', 13 | sauce: undefined, 14 | cheese: undefined, 15 | meats: undefined, 16 | veggies: undefined 17 | }; 18 | 19 | return preparePizza(pizza); 20 | } 21 | 22 | function preparePizza(pizza) { 23 | return pizza; 24 | }" 25 | `; 26 | -------------------------------------------------------------------------------- /src/slice-code/test/__snapshots__/try-catch.test.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`statementsAfterThrow() 1`] = ` 4 | "export { statementsAfterThrow }; 5 | 6 | function statementsAfterThrow() { 7 | const ret = {}; 8 | try { 9 | ret.before = true; 10 | 11 | throw new Error('throw error'); 12 | } catch (error) { 13 | ret.error = error; 14 | } finally { 15 | return ret; 16 | } 17 | }" 18 | `; 19 | 20 | exports[`tryCatch(false) && tryCatch(true) 1`] = ` 21 | "export { tryCatch }; 22 | 23 | function tryCatch(shouldThrow) { 24 | const ret = {}; 25 | try { 26 | if (shouldThrow) { 27 | throw new Error('throw error'); 28 | } 29 | ret.noThrow = true; 30 | } catch (error) { 31 | ret.error = error; 32 | return error; 33 | } finally { 34 | return ret; 35 | } 36 | }" 37 | `; 38 | 39 | exports[`tryCatch(false) 1`] = ` 40 | "export { tryCatch }; 41 | 42 | function tryCatch() { 43 | const ret = {}; 44 | 45 | ret.noThrow = true; 46 | 47 | return ret; 48 | }" 49 | `; 50 | 51 | exports[`tryCatch(true) 1`] = ` 52 | "export { tryCatch }; 53 | 54 | function tryCatch() { 55 | const ret = {}; 56 | try { 57 | throw new Error('throw error'); 58 | } catch (error) { 59 | ret.error = error; 60 | return error; 61 | } finally { 62 | return ret; 63 | } 64 | }" 65 | `; 66 | -------------------------------------------------------------------------------- /src/slice-code/test/assignment-expression.test.js: -------------------------------------------------------------------------------- 1 | import {runAllCombosTests} from './helpers/utils' 2 | 3 | /* 4 | test.only( 5 | 'orAndOr(false, false, true, true)', 6 | require('./helpers/utils').snapSlice( 7 | require.resolve('./fixtures/logical-expressions'), 8 | ({orAndOr}) => { 9 | return [orAndOr(false, false, true, true)] 10 | }, 11 | ), 12 | ) 13 | /* */ 14 | 15 | runAllCombosTests({ 16 | filename: require.resolve('./fixtures/assignment-expressions'), 17 | methods: [ 18 | { 19 | methodName: 'plusEqual', 20 | explicitArgs: [[15, 5]], 21 | }, 22 | { 23 | methodName: 'minusEqual', 24 | explicitArgs: [[15, 5]], 25 | }, 26 | { 27 | methodName: 'divideEqual', 28 | explicitArgs: [[15, 5]], 29 | }, 30 | { 31 | methodName: 'multiplyEqual', 32 | explicitArgs: [[15, 5]], 33 | }, 34 | ], 35 | }) 36 | -------------------------------------------------------------------------------- /src/slice-code/test/assignment-ternary.test.js: -------------------------------------------------------------------------------- 1 | import {runAllCombosTests} from './helpers/utils' 2 | 3 | /* 4 | test.only( 5 | 'orAndOr(false, false, true, true)', 6 | require('./helpers/utils').snapSlice( 7 | require.resolve('./fixtures/logical-expressions'), 8 | ({orAndOr}) => { 9 | return [orAndOr(false, false, true, true)] 10 | }, 11 | ), 12 | ) 13 | /* */ 14 | 15 | runAllCombosTests({ 16 | filename: require.resolve('./fixtures/assignment-ternary'), 17 | methods: [ 18 | { 19 | methodName: 'assignmentTernary', 20 | possibleArguments: [['a'], ['b']], 21 | }, 22 | { 23 | methodName: 'diacriticsClean', 24 | possibleArguments: [['Some apple'], ['Some Google']], 25 | }, 26 | ], 27 | }) 28 | -------------------------------------------------------------------------------- /src/slice-code/test/async-code.test.js: -------------------------------------------------------------------------------- 1 | import {runAllCombosTests, comboOfBools} from './helpers/utils' 2 | 3 | /* 4 | test.only( 5 | 'orAndOr(false, false, true, true)', 6 | require('./helpers/utils').snapSlice( 7 | require.resolve('./fixtures/logical-expressions'), 8 | ({orAndOr}) => { 9 | return [orAndOr(false, false, true, true)] 10 | }, 11 | ), 12 | ) 13 | /* */ 14 | 15 | runAllCombosTests({ 16 | filename: require.resolve('./fixtures/async-code'), 17 | methods: [ 18 | { 19 | methodName: 'callPromise', 20 | possibleArguments: comboOfBools(1), 21 | }, 22 | { 23 | methodName: 'callback', 24 | possibleArguments: comboOfBools(1), 25 | }, 26 | ], 27 | }) 28 | -------------------------------------------------------------------------------- /src/slice-code/test/clone.test.js: -------------------------------------------------------------------------------- 1 | import {runAllCombosTests} from './helpers/utils' 2 | 3 | // this is here to make it easy to isolate tests to a specific case 4 | /* 5 | test.only( 6 | 'testing uncoveredConditionalExpression(false)', 7 | require('./helpers/utils').snapSlice( 8 | require.resolve('./fixtures/cond-expr'), 9 | ({uncoveredConditionalExpression}) => { 10 | uncoveredConditionalExpression(false) 11 | }, 12 | ), 13 | ) 14 | /* */ 15 | 16 | runAllCombosTests({ 17 | filename: require.resolve('./fixtures/clone'), 18 | methods: [ 19 | { 20 | useDefaultExport: true, 21 | methodName: 'clone', 22 | possibleArguments: [ 23 | ['hello'], 24 | [null], 25 | [{name: 'Luke'}], 26 | [{friends: [{name: 'Rebecca'}]}], 27 | [ 28 | { 29 | title: 'How to Train Your Dragon', 30 | releaseDate: new Date('2010-03-26 00:00:00'), 31 | }, 32 | ], 33 | ], 34 | }, 35 | ], 36 | }) 37 | -------------------------------------------------------------------------------- /src/slice-code/test/complex-fns.test.js: -------------------------------------------------------------------------------- 1 | import {runAllCombosTests} from './helpers/utils' 2 | 3 | /* 4 | test.only( 5 | 'get("SomethingElse")', 6 | require('./helpers/utils').snapSlice( 7 | require.resolve('./fixtures/complex-fns'), 8 | ({get}) => { 9 | return [get('SomethingElse')] 10 | }, 11 | ), 12 | ) 13 | /* */ 14 | 15 | runAllCombosTests({ 16 | filename: require.resolve('./fixtures/complex-fns'), 17 | methods: [ 18 | { 19 | methodName: 'get', 20 | possibleArguments: [['Luke'], ['Han']], 21 | }, 22 | ], 23 | }) 24 | -------------------------------------------------------------------------------- /src/slice-code/test/cond-expr.test.js: -------------------------------------------------------------------------------- 1 | import {runAllCombosTests, comboOfBools} from './helpers/utils' 2 | 3 | // this is here to make it easy to isolate tests to a specific case 4 | /* 5 | test.only( 6 | 'testing uncoveredConditionalExpression(false)', 7 | require('./helpers/utils').snapSlice( 8 | require.resolve('./fixtures/cond-expr'), 9 | ({uncoveredConditionalExpression}) => { 10 | uncoveredConditionalExpression(false) 11 | }, 12 | ), 13 | ) 14 | /* */ 15 | 16 | runAllCombosTests({ 17 | filename: require.resolve('./fixtures/cond-expr'), 18 | methods: [ 19 | { 20 | methodName: 'isConsequentOrAlternate', 21 | possibleArguments: comboOfBools(1), 22 | }, 23 | { 24 | methodName: 'isConsequentOrAlternatesConsequentOrAlternate', 25 | possibleArguments: comboOfBools(2), 26 | }, 27 | { 28 | methodName: 'uncoveredConditionalExpression', 29 | possibleArguments: comboOfBools(1), 30 | }, 31 | ], 32 | }) 33 | -------------------------------------------------------------------------------- /src/slice-code/test/early-exit.test.js: -------------------------------------------------------------------------------- 1 | import {runAllCombosTests, comboOfBools} from './helpers/utils' 2 | 3 | // this is here to make it easy to isolate tests to a specific case 4 | /* 5 | test.only( 6 | 'testing ifStatement(false)', 7 | require('./helpers/utils').snapSlice( 8 | require.resolve('./fixtures/early-exit'), 9 | ({ifStatement}) => { 10 | ifStatement(false) 11 | }, 12 | ), 13 | ) 14 | /* */ 15 | 16 | runAllCombosTests({ 17 | filename: require.resolve('./fixtures/early-exit'), 18 | methods: [ 19 | {methodName: 'ifStatement', possibleArguments: comboOfBools(1)}, 20 | {methodName: 'condExpr', possibleArguments: comboOfBools(1)}, 21 | {methodName: 'logicalExpr', possibleArguments: comboOfBools(1)}, 22 | ], 23 | }) 24 | -------------------------------------------------------------------------------- /src/slice-code/test/fixtures/assignment-expressions.js: -------------------------------------------------------------------------------- 1 | export {plusEqual, minusEqual, divideEqual, multiplyEqual} 2 | 3 | function plusEqual(thingOne, thingTwo) { 4 | thingOne += thingTwo 5 | return thingOne 6 | } 7 | 8 | function minusEqual(thingOne, thingTwo) { 9 | thingOne -= thingTwo 10 | return thingOne 11 | } 12 | 13 | function divideEqual(thingOne, thingTwo) { 14 | thingOne /= thingTwo 15 | return thingOne 16 | } 17 | 18 | function multiplyEqual(thingOne, thingTwo) { 19 | thingOne *= thingTwo 20 | return thingOne 21 | } 22 | -------------------------------------------------------------------------------- /src/slice-code/test/fixtures/assignment-ternary.js: -------------------------------------------------------------------------------- 1 | export {assignmentTernary, diacriticsClean} 2 | 3 | const output = { 4 | map: { 5 | a: 'A', 6 | } 7 | } 8 | 9 | function assignmentTernary(letter) { 10 | let string = 'init' 11 | string += letter in output.map ? output.map[letter] : letter 12 | return string 13 | } 14 | 15 | function diacriticsClean(input) { 16 | if (!input || !input.length || input.length < 1) { 17 | return '' 18 | } 19 | 20 | var string = '' 21 | var letters = input.split('') 22 | var index = 0 23 | var length = letters.length 24 | var letter 25 | 26 | for (; index < length; index++) { 27 | letter = letters[index] 28 | string += letter in output.map ? output.map[letter] : letter 29 | } 30 | 31 | return string 32 | } 33 | -------------------------------------------------------------------------------- /src/slice-code/test/fixtures/async-code.js: -------------------------------------------------------------------------------- 1 | export {callPromise, callback} 2 | 3 | function promise(pass) { 4 | return new Promise((resolve, reject) => { 5 | if (pass) { 6 | resolve('pass') 7 | } else { 8 | reject('!pass') 9 | } 10 | }) 11 | } 12 | 13 | function callback(pass, cb) { 14 | return callPromise(pass).then(cb, cb) // I know, I'm cheating :P 15 | } 16 | 17 | function callPromise(pass) { 18 | // do this so we don't get Unhandled promise rejections in the console 19 | return promise(pass).catch(rejection => rejection) 20 | } 21 | -------------------------------------------------------------------------------- /src/slice-code/test/fixtures/clone.js: -------------------------------------------------------------------------------- 1 | export default clone 2 | 3 | function clone(item) { 4 | if (!item) { 5 | return item 6 | } 7 | const type = typeof item 8 | const string = Object.prototype.toString.call(item) 9 | const isPrimitive = type !== "object" && type !== "function" 10 | let result = item 11 | 12 | if (!isPrimitive) { 13 | if (string === '[object Array]') { 14 | result = [] 15 | item.forEach((child, index, array) => { 16 | result[index] = clone(child) 17 | }) 18 | } else if (type === 'object') { 19 | if (item.nodeType && typeof item.cloneNode == 'function') { 20 | result = item.cloneNode(true) 21 | } else if (!item.prototype) { 22 | if (string === '[object Date]') { 23 | result = new Date(item) 24 | } else { 25 | result = {} 26 | for (const i in item) { 27 | result[i] = clone(item[i]) 28 | } 29 | } 30 | } else { 31 | if (false && item.constructor) { 32 | result = new item.constructor() 33 | } else { 34 | result = item 35 | } 36 | } 37 | } 38 | } 39 | 40 | return result 41 | } 42 | -------------------------------------------------------------------------------- /src/slice-code/test/fixtures/complex-fns.js: -------------------------------------------------------------------------------- 1 | export {unusedReturnAssign, get} 2 | 3 | function unusedReturnAssign() { 4 | const foo = {} 5 | function bar(msg) { 6 | return function() { 7 | return `bar: ${msg}` 8 | } 9 | } 10 | foo.getLuke = bar('luke') 11 | foo.getLuke.characterName = 'luke' 12 | 13 | foo.getHan = bar('han') 14 | foo.getHan.characterName = 'han' 15 | 16 | foo.getSomethingElse = function() { 17 | return 'something else' 18 | } 19 | 20 | return foo 21 | } 22 | 23 | function get(name) { 24 | return unusedReturnAssign()[`get${name}`]() 25 | } 26 | -------------------------------------------------------------------------------- /src/slice-code/test/fixtures/cond-expr.js: -------------------------------------------------------------------------------- 1 | export {isConsequentOrAlternate, isConsequentOrAlternatesConsequentOrAlternate, uncoveredConditionalExpression} 2 | 3 | function isConsequentOrAlternate(isConsequent) { 4 | return isConsequent ? isConsequent : !isConsequent 5 | } 6 | 7 | function isConsequentOrAlternatesConsequentOrAlternate(isConsequent, isAlternatesConsequent) { 8 | return isConsequent ? isConsequent : isAlternatesConsequent ? isAlternatesConsequent : !isConsequent && !isAlternatesConsequent 9 | } 10 | 11 | function uncoveredConditionalExpression(cover) { 12 | if (cover) { 13 | return cover ? cover : !cover 14 | } 15 | return !cover 16 | } 17 | -------------------------------------------------------------------------------- /src/slice-code/test/fixtures/early-exit.js: -------------------------------------------------------------------------------- 1 | export {ifStatement, condExpr, logicalExpr} 2 | 3 | function ifStatement(passIf) { 4 | if (!passIf) { 5 | return 'did not reach IfStatement' 6 | } 7 | if (passIf) { 8 | return 'reached IfStatement' 9 | } 10 | } 11 | 12 | function condExpr(passIf) { 13 | if (!passIf) { 14 | return 'did not reach IfStatement' 15 | } 16 | return passIf ? 'reached CondExpr Consequent' : 'reached CondExpr Alternate' 17 | } 18 | 19 | function logicalExpr(passIf) { 20 | if (!passIf) { 21 | return 'did not reach IfStatement' 22 | } 23 | return passIf && 'reached LogicalExpression' 24 | } 25 | -------------------------------------------------------------------------------- /src/slice-code/test/fixtures/functions.js: -------------------------------------------------------------------------------- 1 | export {sum, subtract, multiply, divide} 2 | 3 | function sum(a, b) { 4 | return a + b 5 | } 6 | 7 | function subtract(a, b) { 8 | return sum(a, -b) 9 | } 10 | 11 | function multiply(a, b) { 12 | let product, i 13 | product = 0 14 | for (i = 0; i < b; i++) { 15 | product = sum(product, a) 16 | } 17 | return product 18 | } 19 | 20 | function divide(a, b) { 21 | return a / b 22 | } 23 | -------------------------------------------------------------------------------- /src/slice-code/test/fixtures/if-statement-side-effects.js: -------------------------------------------------------------------------------- 1 | export {ifWithAssignment, ifWithFunctionCall} 2 | 3 | function ifWithAssignment(passIf) { 4 | let x 5 | if ((x = 'hi') && passIf) { 6 | return x + passIf 7 | } else { 8 | return !passIf + x 9 | } 10 | } 11 | 12 | function ifWithFunctionCall(passIf) { 13 | let thing 14 | const setThing = a => (thing = a, passIf) 15 | if (passIf && setThing('hey') && someOtherThing(passIf)) { 16 | return thing + passIf 17 | } else if (!someOtherThing(passIf) || passIf) { 18 | return !passIf 19 | } else { 20 | return passIf 21 | } 22 | 23 | function someOtherThing(a) { 24 | return a 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/slice-code/test/fixtures/if-statements.js: -------------------------------------------------------------------------------- 1 | export {ifOnly, ifElse, ifElseIfElse, nestedIf} 2 | 3 | function ifOnly(passIf) { 4 | if (passIf) { 5 | return passIf 6 | } 7 | } 8 | 9 | function ifElse(passIf) { 10 | if (passIf) { 11 | return passIf 12 | } else { 13 | return !passIf 14 | } 15 | } 16 | 17 | function ifElseIfElse(passIf1, passIf2) { 18 | if (passIf1) { 19 | return passIf1 20 | } else if (passIf2) { 21 | return passIf2 22 | } else { 23 | return !passIf1 && !passIf2 24 | } 25 | } 26 | 27 | function nestedIf(passIf1, passIf2) { 28 | if (passIf1) { 29 | if (passIf2) { 30 | return passIf2 31 | } 32 | return passIf1 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/slice-code/test/fixtures/logical-expressions.js: -------------------------------------------------------------------------------- 1 | export const allAnd = (a, b, c) => a && b && c 2 | export const allOr = (a, b, c) => a || b || c 3 | export const andOr = (a, b, c) => a && b || c 4 | export const orAndOr = (a, b, c, d) => (a || b) && (c || d) 5 | export const andOrAnd = (a, b, c, d) => (a && b) || (c && d) 6 | -------------------------------------------------------------------------------- /src/slice-code/test/fixtures/module-pattern.js: -------------------------------------------------------------------------------- 1 | export default (() => { 2 | const mod = {} 3 | 4 | mod.foo = () => 'foo' 5 | 6 | mod.bar = 'bar' 7 | 8 | mod.baz = returnBaz => (returnBaz ? 'baz' : 'buzz') 9 | 10 | mod.foobar = function() { 11 | return 'function assignment' 12 | } 13 | 14 | mod.mokeypatchFn = function() { 15 | return '🐒' 16 | } 17 | mod.mokeypatchFn.favorite = 'my favorite animal is a 🐒' 18 | 19 | mod.monkeypatchArrow = () => { 20 | return '🐵' 21 | } 22 | mod.monkeypatchArrow.favorite = 'my favorite animal is a 🐵' 23 | 24 | return mod 25 | })() 26 | -------------------------------------------------------------------------------- /src/slice-code/test/fixtures/pizza.js: -------------------------------------------------------------------------------- 1 | export {makePizza} 2 | 3 | const pizzaTypes = [ 4 | 'cheese', 5 | 'pepperoni', 6 | 'meatLovers', 7 | 'supreme', 8 | ] 9 | 10 | const getCrust = (size, crustType) => `${size}: ${crustType || 'Hand tossed pizza'}` 11 | const getSauce = (size, sauceType) => `${size}: ${sauceType}` 12 | const getCheese = (size, cheeseType) => `${size}: ${cheeseType} cheese` 13 | const getMeats = (size, meats) => `${size}: ${meats.join(', ')}` 14 | const getVeggies = (size, veggies) => `${size}: ${veggies.join(', ')}` 15 | 16 | function makePizza(order) { 17 | if (!pizzaTypes.includes(order.type)) { 18 | throw new Error(`${order.type} pizza 🍕 is not available 😦`) 19 | } 20 | const pizza = { 21 | type: order.type, 22 | crust: getCrust(order.size, order.crustType), 23 | crustEdge: order.crustEdge || 'Garlic Buttery Blend', 24 | sauce: order.sauceType ? getSauce(order.size, order.sauceType) : undefined, 25 | cheese: order.cheeseType ? getCheese(order.size, order.cheeseType) : undefined, 26 | meats: order.meats ? getMeats(order.size, order.meats) : undefined, 27 | veggies: order.veggies ? getVeggies(order.size, order.veggies) : undefined, 28 | } 29 | 30 | return preparePizza(pizza) 31 | } 32 | 33 | function preparePizza(pizza) { 34 | // todo... 35 | return pizza 36 | } 37 | -------------------------------------------------------------------------------- /src/slice-code/test/fixtures/switch-statement.js: -------------------------------------------------------------------------------- 1 | export {switchWithFallThrough, switchWithDefault, switchWithSideEffects} 2 | 3 | function switchWithFallThrough(color) { 4 | let ret = null 5 | switch (color) { 6 | case 'green': 7 | case 'blue': 8 | case 'purple': 9 | ret = `1st ${color}` 10 | break 11 | case 'yellow': 12 | case 'orange': 13 | case 'red': 14 | ret = `2nd ${color}` 15 | } 16 | return ret 17 | } 18 | 19 | function switchWithDefault(candy) { 20 | switch(candy) { 21 | case 'twix': 22 | return `case ${candy}` 23 | default: 24 | return 'default, no candy :-(' 25 | } 26 | } 27 | 28 | function switchWithSideEffects(character) { 29 | switch(sideEffect(character)) { 30 | case 'harry': 31 | return ['main', character] 32 | default: 33 | return ['supporting', character] 34 | } 35 | 36 | function sideEffect(obj) { 37 | obj.touched = true 38 | return obj.name 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/slice-code/test/fixtures/try-catch.js: -------------------------------------------------------------------------------- 1 | export {tryCatch, statementsAfterThrow} 2 | 3 | function tryCatch(shouldThrow) { 4 | const ret = {} 5 | try { 6 | if (shouldThrow) { 7 | throw new Error('throw error') 8 | } 9 | ret.noThrow = true 10 | } catch (error) { 11 | ret.error = error 12 | return error 13 | } finally { 14 | return ret 15 | } 16 | } 17 | 18 | function statementsAfterThrow() { 19 | const ret = {} 20 | try { 21 | ret.before = true 22 | if (ret.before) { 23 | throw new Error('throw error') 24 | } 25 | ret.after = true 26 | } catch (error) { 27 | ret.error = error 28 | } finally { 29 | return ret 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/slice-code/test/fixtures/unused-assignment.js: -------------------------------------------------------------------------------- 1 | export {unusedAssignment, dependencies, sortRankedItems} 2 | 3 | function unusedAssignment(a, b, c) { 4 | const aIndex = a.index 5 | const {index: bIndex} = b 6 | const {index} = c 7 | return aIndex > bIndex ? 'aIndex' : bIndex > index ? 'bIndex' : 'c.index' 8 | } 9 | 10 | function dependencies(foo, bar) { 11 | const [firstFoo] = foo 12 | const [firstBar] = bar 13 | const foobar = firstFoo === firstBar 14 | if (foobar) { 15 | return 'same' 16 | } else { 17 | return 'different' 18 | } 19 | } 20 | 21 | function sortRankedItems(a, b) { 22 | var {rank: aRank} = a 23 | var bRank = b.rank 24 | 25 | var same = aRank === bRank 26 | if (same) { 27 | return 'same' 28 | } else { 29 | return 'not same' 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/slice-code/test/functions.test.js: -------------------------------------------------------------------------------- 1 | import {curry} from 'lodash' 2 | import combs from 'combs' 3 | import {snapSlice, runAllCombosTests} from './helpers/utils' 4 | 5 | const functionsFilename = require.resolve('./fixtures/functions') 6 | const snapFunctionsSlice = curry(snapSlice)(functionsFilename) 7 | 8 | // this is here to make it easy to isolate tests to a specific case 9 | // test.only( 10 | // 'sum(1, 2)', 11 | // snapFunctionsSlice(({sum}) => { 12 | // return [sum(1, 2)] 13 | // }) 14 | // ) 15 | 16 | const methodCombos = combs(['sum', 'subtract', 'multiply', 'divide']) 17 | 18 | methodCombos.forEach(methods => { 19 | test( 20 | methods.map(method => `${method}(1, 2)`).join(' && '), 21 | snapFunctionsSlice(module => { 22 | methods.forEach(method => { 23 | module[method](1, 2) 24 | }) 25 | }), 26 | ) 27 | }) 28 | 29 | runAllCombosTests({ 30 | filename: functionsFilename, 31 | methods: [ 32 | {methodName: 'sum', possibleArguments: [[1, 2]]}, 33 | {methodName: 'subtract', possibleArguments: [[1, 2]]}, 34 | {methodName: 'multiply', possibleArguments: [[1, 2]]}, 35 | {methodName: 'divide', possibleArguments: [[1, 2]]}, 36 | ], 37 | }) 38 | -------------------------------------------------------------------------------- /src/slice-code/test/helpers/utils.js: -------------------------------------------------------------------------------- 1 | /* istanbul ignore next */ 2 | import fs from 'fs' 3 | import Module from 'module' 4 | import path from 'path' 5 | import combs from 'combs' 6 | import * as babel from 'babel-core' 7 | import {programVisitor as getInstrumentVisitor} from 'istanbul-lib-instrument' 8 | import {random} from 'lodash' 9 | import template from 'babel-template' 10 | import transformCoverage from '../../transform-coverage' 11 | import sliceCode from '../..' 12 | 13 | const coverageVariable = '____sliceCoverage____' 14 | 15 | export { 16 | comboOfBools, 17 | comboOfItems, 18 | snapSlice, 19 | runAllCombosTests, 20 | snapSliceCode, 21 | getSliceAndInfo, 22 | } 23 | 24 | function comboOfBools(n) { 25 | const len = (Math.pow(2, n) - 1).toString(2).length 26 | const result = [] 27 | for (let i = 0; i < Math.pow(2, n); i++) { 28 | const val = i.toString(2) 29 | const missing = len - val.length 30 | result.push( 31 | Array.from({length: missing}) 32 | .map(() => false) 33 | .concat(Array.from(val).map(v => v === '1')), 34 | ) 35 | } 36 | return result 37 | } 38 | 39 | /** 40 | * @param {Array} items the items to get combinations for 41 | * @return {Array} an array of arrays of the possible combination of those items 42 | */ 43 | function comboOfItems(items) { 44 | if (items.length < 2) { 45 | return [items] 46 | } 47 | const combos = [] 48 | items.forEach((item, index) => { 49 | const firstHalf = items.slice(0, index) 50 | const secondHalf = items.slice(index + 1) 51 | const remainingCombos = comboOfItems([...firstHalf, ...secondHalf]) 52 | remainingCombos.forEach(combo => { 53 | combos.push([item, ...combo]) 54 | }) 55 | }) 56 | return combos 57 | } 58 | 59 | function snapSlice(relativePath, tester) { 60 | const absolutePath = require.resolve(relativePath) 61 | const sourceCode = fs.readFileSync(absolutePath, 'utf8') 62 | return snapSliceCode(sourceCode, tester, absolutePath) 63 | } 64 | 65 | function snapSliceCode(sourceCode, tester, actualFilepath) { 66 | // the function returned here is what you'd 67 | // place in a call to Jest's `test` function 68 | return async () => { 69 | const { 70 | originalResult, 71 | slicedCode, 72 | isSlicedCoverage100, 73 | slicedResult, 74 | } = await getSliceAndInfo(sourceCode, tester, actualFilepath) 75 | expect(slicedCode).toMatchSnapshot() 76 | expect(isSlicedCoverage100).toBe(true, 'coverage should be 100%') 77 | expect(originalResult).toEqual( 78 | slicedResult, 79 | 'originalResult should be the same as the slicedResult', 80 | ) 81 | } 82 | } 83 | 84 | async function getSliceAndInfo(sourceCode, tester, actualFilepath) { 85 | const tempFilename = `./temp-sliced.${random(1, 9999999999999)}.js` 86 | const mod = getInstrumentedModuleFromString( 87 | tempFilename, 88 | sourceCode, 89 | actualFilepath, 90 | ) 91 | const originalResult = await tester(mod) 92 | // console.log('originalResult', originalResult) 93 | const coverageData = mod[coverageVariable][tempFilename] 94 | const slicedCode = sliceCode(sourceCode, coverageData) 95 | const filteredCoverage = transformCoverage(coverageData) 96 | // console.log('slicedCode', slicedCode) 97 | const {is100: isSlicedCoverage100, slicedResult} = await slicedCoverageIs100( 98 | tempFilename, 99 | slicedCode, 100 | tester, 101 | actualFilepath, 102 | ) 103 | return { 104 | mod, 105 | originalResult, 106 | slicedCode, 107 | isSlicedCoverage100, 108 | slicedResult, 109 | filteredCoverage, 110 | } 111 | } 112 | 113 | function runAllCombosTests({filename, methods}) { 114 | methods.forEach(({ 115 | methodName, 116 | useDefaultExport, 117 | possibleArguments, 118 | explicitArgs, 119 | }) => { 120 | if (explicitArgs) { 121 | explicitArgs.forEach(args => { 122 | const title = `${methodName}(${args 123 | .map(a => JSON.stringify(a)) 124 | .join(', ')})` 125 | test( 126 | title, 127 | snapSlice(filename, mod => { 128 | const method = useDefaultExport ? mod : mod[methodName] 129 | return method(...args) 130 | }), 131 | ) 132 | }) 133 | } else { 134 | const possibleCombinations = combs(possibleArguments) 135 | possibleCombinations.forEach(generateTests) 136 | } 137 | 138 | function generateTests(comboOfArgs) { 139 | // generate the message for the test title 140 | const testTitle = comboOfArgs 141 | .map(args => { 142 | return `${methodName}(${args 143 | .map(a => JSON.stringify(a)) 144 | .join(', ')})` 145 | }) 146 | .join(' && ') 147 | 148 | // this is the call to Jest's `test` function 149 | let test = global.test 150 | if (!test) { 151 | test = (title, fn) => fn() 152 | } 153 | test( 154 | testTitle, 155 | snapSlice(filename, mod => { 156 | const method = useDefaultExport ? 157 | mod.default || mod : 158 | mod[methodName] 159 | /* 160 | console.log( 161 | useDefaultExport, 162 | methodName, 163 | Object.keys(mod), 164 | typeof method, 165 | ) 166 | /* */ 167 | return comboOfArgs.map(args => method(...args)) 168 | }), 169 | ) 170 | } 171 | }) 172 | } 173 | 174 | async function slicedCoverageIs100( 175 | filename, 176 | slicedCode, 177 | tester, 178 | actualFilepath, 179 | ) { 180 | const mod = getInstrumentedModuleFromString( 181 | filename, 182 | slicedCode, 183 | actualFilepath, 184 | ) 185 | const slicedResult = await tester(mod) 186 | /* 187 | process.stdout.write( 188 | `\n\nmod[coverageVariable][filename].s\n\n${ 189 | JSON.stringify(mod[coverageVariable][filename].s, null, 2)}`, 190 | ) 191 | /* */ 192 | const is100 = coverageIs100Percent(mod[coverageVariable]) 193 | return {slicedResult, is100} 194 | 195 | function coverageIs100Percent(coverageData) { 196 | const cov = coverageData[filename] 197 | const functions100 = Object.keys(cov.f).every(k => cov.f[k] > 0) 198 | const statements100 = Object.keys(cov.s).every(k => cov.s[k] > 0) 199 | const branches100 = Object.keys(cov.b).every( 200 | k => cov.b[k][0] > 0 && cov.b[k][1] > 0, 201 | ) 202 | return functions100 && statements100 && branches100 203 | } 204 | } 205 | 206 | function getInstrumentedModuleFromString(filename, sourceCode, actualFilepath) { 207 | // for the original source, we don't want to ignore anything 208 | // but there are some cases where we have to create 209 | // empty functions that aren't covered 210 | // to ensure that the resulting code functions properly. 211 | // So we add an obnoxiously long comment 212 | // and replace it here. 213 | const sourceCodeWithoutIstanbulPragma = sourceCode 214 | .replace(/istanbul/g, 'ignore-istanbul-ignore') 215 | .replace(/slice-js-coverage-ignore/g, 'istanbul') 216 | const {code} = babel.transform(sourceCodeWithoutIstanbulPragma, { 217 | filename, 218 | babelrc: false, 219 | // compact: false, 220 | only: filename, 221 | presets: ['node6', 'stage-2'], 222 | plugins: [instrumenter], 223 | }) 224 | // process.stdout.write('\n\ninstrumentedCode\n\n' + code) 225 | return requireFromString(code, actualFilepath || filename) 226 | } 227 | 228 | /* 229 | * copied and modified from require-from-string 230 | */ 231 | function requireFromString(code, filepath) { 232 | const m = new Module(filepath, module.parent) 233 | m.filename = filepath 234 | m.paths = Module._nodeModulePaths(path.dirname(filepath)) 235 | m._compile(code, filepath) 236 | return m.exports 237 | } 238 | 239 | function instrumenter({types: t}) { 240 | return { 241 | visitor: { 242 | Program: { 243 | enter(...args) { 244 | this.__dv__ = getInstrumentVisitor(t, this.file.opts.filename, { 245 | coverageVariable, 246 | }) 247 | this.__dv__.enter(...args) 248 | }, 249 | exit(...args) { 250 | this.__dv__.exit(...args) 251 | // expose coverage as part of the module 252 | const newNode = template( 253 | `module.exports.${coverageVariable} = global.${coverageVariable};`, 254 | )() 255 | args[0].node.body.push(newNode) 256 | }, 257 | }, 258 | }, 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /src/slice-code/test/if-statement-side-effects.test.js: -------------------------------------------------------------------------------- 1 | import {runAllCombosTests, comboOfBools} from './helpers/utils' 2 | 3 | /* 4 | test.only( 5 | 'ifWithAssignment(true)', 6 | require('./helpers/utils').snapSlice( 7 | require.resolve('./fixtures/if-statement-side-effects'), 8 | ({ifWithAssignment}) => { 9 | return [ifWithAssignment(true)] 10 | }, 11 | ), 12 | ) 13 | /* */ 14 | 15 | runAllCombosTests({ 16 | filename: require.resolve('./fixtures/if-statement-side-effects'), 17 | methods: [ 18 | {methodName: 'ifWithAssignment', possibleArguments: comboOfBools(1)}, 19 | {methodName: 'ifWithFunctionCall', possibleArguments: comboOfBools(1)}, 20 | ], 21 | }) 22 | -------------------------------------------------------------------------------- /src/slice-code/test/if-statements.test.js: -------------------------------------------------------------------------------- 1 | import {runAllCombosTests, comboOfBools} from './helpers/utils' 2 | 3 | /* 4 | test.only( 5 | 'ifElse(true)', 6 | require('./helpers/utils').snapSlice( 7 | require.resolve('./fixtures/if-statements'), 8 | ({ifElse}) => { 9 | ifElse(true) 10 | }, 11 | ), 12 | ) 13 | /* */ 14 | 15 | runAllCombosTests({ 16 | filename: require.resolve('./fixtures/if-statements'), 17 | methods: [ 18 | {methodName: 'ifOnly', possibleArguments: comboOfBools(1)}, 19 | {methodName: 'ifElse', possibleArguments: comboOfBools(1)}, 20 | { 21 | methodName: 'ifElseIfElse', 22 | possibleArguments: comboOfBools(2), 23 | }, 24 | { 25 | methodName: 'nestedIf', 26 | possibleArguments: comboOfBools(2), 27 | }, 28 | ], 29 | }) 30 | -------------------------------------------------------------------------------- /src/slice-code/test/indent-string.test.js: -------------------------------------------------------------------------------- 1 | import {runAllCombosTests} from './helpers/utils' 2 | 3 | /* 4 | test.only( 5 | 'orAndOr(false, false, true, true)', 6 | require('./helpers/utils').snapSlice( 7 | require.resolve('./fixtures/logical-expressions'), 8 | ({orAndOr}) => { 9 | return [orAndOr(false, false, true, true)] 10 | }, 11 | ), 12 | ) 13 | /* */ 14 | 15 | runAllCombosTests({ 16 | filename: require.resolve('indent-string'), 17 | methods: [ 18 | { 19 | useDefaultExport: true, 20 | methodName: 'indentString', 21 | possibleArguments: [ 22 | ['hello world'], 23 | ['hello world', 2], 24 | ['hello world', undefined, '@'], 25 | ['hello world', 4, '@'], 26 | ], 27 | }, 28 | ], 29 | }) 30 | -------------------------------------------------------------------------------- /src/slice-code/test/logical-expressions.test.js: -------------------------------------------------------------------------------- 1 | import {runAllCombosTests, comboOfBools} from './helpers/utils' 2 | 3 | /* 4 | test.only( 5 | 'orAndOr(false, false, true, true)', 6 | require('./helpers/utils').snapSlice( 7 | require.resolve('./fixtures/logical-expressions'), 8 | ({orAndOr}) => { 9 | return [orAndOr(false, false, true, true)] 10 | }, 11 | ), 12 | ) 13 | /* */ 14 | 15 | runAllCombosTests({ 16 | filename: require.resolve('./fixtures/logical-expressions'), 17 | methods: [ 18 | {methodName: 'allAnd', explicitArgs: comboOfBools(3)}, 19 | {methodName: 'allOr', explicitArgs: comboOfBools(3)}, 20 | {methodName: 'andOr', explicitArgs: comboOfBools(3)}, 21 | {methodName: 'orAndOr', explicitArgs: comboOfBools(4)}, 22 | {methodName: 'andOrAnd', explicitArgs: comboOfBools(4)}, 23 | ], 24 | }) 25 | -------------------------------------------------------------------------------- /src/slice-code/test/match-sorter.test.js: -------------------------------------------------------------------------------- 1 | import {runAllCombosTests} from './helpers/utils' 2 | 3 | /* 4 | test.only( 5 | 'orAndOr(false, false, true, true)', 6 | require('./helpers/utils').snapSlice( 7 | require.resolve('./fixtures/logical-expressions'), 8 | ({orAndOr}) => { 9 | return [orAndOr(false, false, true, true)] 10 | }, 11 | ), 12 | ) 13 | /* */ 14 | 15 | runAllCombosTests({ 16 | filename: require.resolve('match-sorter'), 17 | methods: [ 18 | { 19 | useDefaultExport: true, 20 | methodName: 'matchSorter', 21 | explicitArgs: [[['hi', 'hey', 'hello', 'sup', 'yo'], 'y']], 22 | }, 23 | ], 24 | }) 25 | -------------------------------------------------------------------------------- /src/slice-code/test/module-pattern.test.js: -------------------------------------------------------------------------------- 1 | import {snapSlice} from './helpers/utils' 2 | 3 | const tests = [ 4 | ({default: mod}) => [mod.foo()], 5 | ({default: mod}) => [mod.foo(), mod.baz(false)], 6 | ({default: mod}) => [mod.foo(), mod.baz(true)], 7 | ({default: mod}) => [mod.baz(true)], 8 | ({default: mod}) => [mod.baz(false)], 9 | ({default: mod}) => [mod.baz(false)], 10 | ({default: mod}) => [mod.foobar()], 11 | ({default: mod}) => [mod.foobar(), mod.baz(false)], 12 | ({default: mod}) => [mod.foobar(), mod.foo()], 13 | ] 14 | 15 | tests.forEach(tester => { 16 | test( 17 | tester.toString(), 18 | snapSlice(require.resolve('./fixtures/module-pattern'), tester), 19 | ) 20 | }) 21 | -------------------------------------------------------------------------------- /src/slice-code/test/pizza.test.js: -------------------------------------------------------------------------------- 1 | import {runAllCombosTests} from './helpers/utils' 2 | 3 | /* 4 | test.only( 5 | 'tryCatch(false)', 6 | require('./helpers/utils').snapSlice( 7 | require.resolve('./fixtures/try-catch'), 8 | ({tryCatch}) => { 9 | return [tryCatch(false)] 10 | }, 11 | ), 12 | ) 13 | /* */ 14 | 15 | runAllCombosTests({ 16 | filename: require.resolve('./fixtures/pizza'), 17 | methods: [ 18 | { 19 | methodName: 'makePizza', 20 | possibleArguments: [[{type: 'cheese', size: 'Large'}]], 21 | }, 22 | ], 23 | }) 24 | -------------------------------------------------------------------------------- /src/slice-code/test/switch-statement.test.js: -------------------------------------------------------------------------------- 1 | import {runAllCombosTests} from './helpers/utils' 2 | 3 | /* 4 | test.only( 5 | 'switchWithSideEffects({"name":"harry"})', 6 | require('./helpers/utils').snapSlice( 7 | require.resolve('./fixtures/switch-statement'), 8 | ({switchWithSideEffects}) => { 9 | return [switchWithSideEffects({name: 'harry'})] 10 | }, 11 | ), 12 | ) 13 | /* */ 14 | 15 | runAllCombosTests({ 16 | filename: require.resolve('./fixtures/switch-statement'), 17 | methods: [ 18 | { 19 | methodName: 'switchWithFallThrough', 20 | possibleArguments: [ 21 | ['green'], 22 | ['blue'], 23 | ['purple'], 24 | ['yellow'], 25 | ['orange'], 26 | ['red'], 27 | ], 28 | }, 29 | { 30 | methodName: 'switchWithDefault', 31 | possibleArguments: [['twix'], ['snickers']], 32 | }, 33 | { 34 | methodName: 'switchWithSideEffects', 35 | possibleArguments: [[{name: 'harry'}], [{name: 'ron'}]], 36 | }, 37 | ], 38 | }) 39 | -------------------------------------------------------------------------------- /src/slice-code/test/try-catch.test.js: -------------------------------------------------------------------------------- 1 | import {runAllCombosTests, comboOfBools} from './helpers/utils' 2 | 3 | /* 4 | test.only( 5 | 'tryCatch(false)', 6 | require('./helpers/utils').snapSlice( 7 | require.resolve('./fixtures/try-catch'), 8 | ({tryCatch}) => { 9 | return [tryCatch(false)] 10 | }, 11 | ), 12 | ) 13 | /* */ 14 | 15 | runAllCombosTests({ 16 | filename: require.resolve('./fixtures/try-catch'), 17 | methods: [ 18 | {methodName: 'tryCatch', possibleArguments: comboOfBools(1)}, 19 | {methodName: 'statementsAfterThrow', explicitArgs: [[]]}, 20 | ], 21 | }) 22 | -------------------------------------------------------------------------------- /src/slice-code/test/unused-assignment.test.js: -------------------------------------------------------------------------------- 1 | import {runAllCombosTests, comboOfItems, comboOfBools} from './helpers/utils' 2 | 3 | /* 4 | test.only( 5 | 'dependencies([false], [false]) && dependencies([false], [true])', 6 | require('./helpers/utils').snapSlice( 7 | require.resolve('./fixtures/unused-assignment'), 8 | ({dependencies}) => { 9 | return [dependencies([false], [false]), dependencies([false], [true])] 10 | }, 11 | ), 12 | ) 13 | /* */ 14 | 15 | const boolsInArrays = comboOfBools(2).map(([first, second]) => [ 16 | [first], 17 | [second], 18 | ]) 19 | 20 | runAllCombosTests({ 21 | filename: require.resolve('./fixtures/unused-assignment'), 22 | methods: [ 23 | { 24 | methodName: 'unusedAssignment', 25 | possibleArguments: comboOfItems([{index: 0}, {index: 1}, {index: 2}]), 26 | }, 27 | { 28 | methodName: 'dependencies', 29 | possibleArguments: boolsInArrays, 30 | }, 31 | { 32 | methodName: 'sortRankedItems', 33 | possibleArguments: [[{rank: 1}, {rank: 1}], [{rank: 2}, {rank: 1}]], 34 | }, 35 | ], 36 | }) 37 | -------------------------------------------------------------------------------- /src/slice-code/transform-coverage.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash' 2 | 3 | export default filterToRunStatementsFunctionsAndBranches 4 | 5 | function filterToRunStatementsFunctionsAndBranches(coverageData) { 6 | const clone = _.cloneDeep(coverageData) 7 | clone.s = filterToRunCodeOnly(clone.s) 8 | clone.f = filterToRunCodeOnly(clone.f) 9 | clone.b = filterToRunCodeOnly(clone.b) 10 | clone.statementMap = filterMapToRunOnly(clone.statementMap, clone.s) 11 | clone.fnMap = filterMapToRunOnly(clone.fnMap, clone.f) 12 | clone.branchMap = filterMapToRunOnly(clone.branchMap, clone.b) 13 | clone.branchMap = annotateBranches(clone.branchMap, clone.b) 14 | return clone 15 | } 16 | 17 | function filterToRunCodeOnly(obj) { 18 | return _.reduce( 19 | obj, 20 | (newObj, val, key) => { 21 | if (isRunBranch(val) || (_.isNumber(val) && val !== 0)) { 22 | newObj[key] = val 23 | } 24 | return newObj 25 | }, 26 | {}, 27 | ) 28 | } 29 | 30 | function isRunBranch(val) { 31 | return Array.isArray(val) && val.some(i => !!i) 32 | } 33 | 34 | function filterMapToRunOnly(map, indexesRun) { 35 | return Object.keys(indexesRun).reduce( 36 | (newObj, indexRun) => { 37 | newObj[indexRun] = map[indexRun] 38 | return newObj 39 | }, 40 | {}, 41 | ) 42 | } 43 | 44 | function annotateBranches(branchMap, branchesRun) { 45 | const clone = _.cloneDeep(branchMap) 46 | _.forEach(clone, (branch, key) => { 47 | const run = branchesRun[key] 48 | branch.locations.forEach((location, index) => { 49 | location.covered = run[index] > 0 50 | }) 51 | // binary expressions don't have a concept of consequent or alternate 52 | if (branch.type !== 'binary-expr') { 53 | const [conLoc, altLoc] = branch.locations 54 | branch.consequent = {covered: run[0] > 0, loc: conLoc} 55 | branch.alternate = {covered: run[1] > 0, loc: altLoc} 56 | } 57 | }) 58 | return clone 59 | } 60 | -------------------------------------------------------------------------------- /test/fixtures/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["node6", "stage-2"], 3 | "plugins": [ 4 | ["istanbul", { 5 | "exclude": [ 6 | "*.+(test|stub).*" 7 | ] 8 | }] 9 | ] 10 | } 11 | -------------------------------------------------------------------------------- /test/fixtures/module.js: -------------------------------------------------------------------------------- 1 | /* 2 | * this file may seem odd, but trust me, this is intended to help 3 | * ensure we're developing a good slice, not to have a good math 4 | * module :) 5 | */ 6 | export {sum, subtract, multiply, divide, isGreaterThan, isFooOrBar} 7 | 8 | function sum(a, b) { 9 | return a + b 10 | } 11 | 12 | function subtract(a, b) { 13 | return sum(a, -b) 14 | } 15 | 16 | function multiply(a, b) { 17 | let product, i 18 | product = 0 19 | for (i = 0; i < b; i++) { 20 | product = sum(product, a) 21 | } 22 | return product 23 | } 24 | 25 | function divide(a, b) { 26 | return a / b 27 | } 28 | 29 | function isGreaterThan(a, b) { 30 | if (a > b) { 31 | return true 32 | } else if (b > a) { 33 | return false 34 | } else { 35 | return null 36 | } 37 | } 38 | 39 | function isFooOrBar(a) { 40 | /* eslint no-nested-ternary:0, no-unneeded-ternary:0 */ 41 | return a === 'foo' ? true : a === 'bar' ? true : false 42 | } 43 | -------------------------------------------------------------------------------- /test/fixtures/module.slicer.js: -------------------------------------------------------------------------------- 1 | import slice from '../../src' 2 | 3 | // this is just here to make it so we can generate a coverage report with jest easily 4 | if (global.test) { 5 | test('works', () => {}) 6 | } 7 | 8 | slice(require.resolve('./module'), 'multiply multiplies numbers together', ({multiply}) => { 9 | return [multiply(3, 2)] 10 | }) 11 | --------------------------------------------------------------------------------