├── .all-contributorsrc ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── .npmrc ├── .travis.yml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── macro.js ├── package.json └── src ├── __tests__ ├── __snapshots__ │ └── index.js.snap └── index.js └── index.js /.all-contributorsrc: -------------------------------------------------------------------------------- 1 | { 2 | "projectName": "babel-plugin-sourcer", 3 | "projectOwner": "tannerlinsley", 4 | "files": [ 5 | "README.md" 6 | ], 7 | "imageSize": 100, 8 | "commit": false, 9 | "contributors": [ 10 | { 11 | "login": "tannerlinsley", 12 | "name": "Tanner Linsley", 13 | "avatar_url": "https://avatars0.githubusercontent.com/u/5580297?v=4", 14 | "profile": "http://Nozzle.io", 15 | "contributions": [ 16 | "code", 17 | "design", 18 | "doc", 19 | "example" 20 | ] 21 | }, 22 | { 23 | "login": "kentcdodds", 24 | "name": "Kent C. Dodds", 25 | "avatar_url": "https://avatars0.githubusercontent.com/u/1500684?v=4", 26 | "profile": "https://kentcdodds.com", 27 | "contributions": [ 28 | "question", 29 | "code", 30 | "ideas", 31 | "plugin", 32 | "talk", 33 | "tool" 34 | ] 35 | } 36 | ], 37 | "repoType": "github" 38 | } 39 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | *.js text eol=lf 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 13 | 14 | * `babel-plugin-source` version: 15 | * `node` version: 16 | * `npm` (or `yarn`) version: 17 | 18 | Relevant code or config 19 | 20 | ```javascript 21 | ``` 22 | 23 | What you did: 24 | 25 | What happened: 26 | 27 | 28 | 29 | Reproduction repository: 30 | 31 | 35 | 36 | Problem description: 37 | 38 | Suggested solution: 39 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | **What**: 20 | 21 | 22 | 23 | **Why**: 24 | 25 | 26 | 27 | **How**: 28 | 29 | 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | coverage 3 | dist 4 | .opt-in 5 | .opt-out 6 | *.log 7 | 8 | package-lock.json 9 | yarn.lock 10 | .history -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=http://registry.npmjs.org/ 2 | package-lock=false 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | cache: 4 | directories: 5 | - ~/.npm 6 | notifications: 7 | email: false 8 | node_js: '8' 9 | install: npm install 10 | script: npm run validate 11 | branches: 12 | only: master 13 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | ## 0.0.5-6 4 | 5 | - Handle empty body edge-case bug 6 | 7 | ## 0.0.4 8 | 9 | - Handle empty comments 10 | 11 | ## 0.0.3 12 | 13 | - Use recursion to handle unmatched comments 14 | 15 | ## 0.0.2 16 | 17 | - Source strings will no longer contain generated code from babel (eg. semi-colons, etc). 18 | - Source strings now truncate lines to the shortest indentation 19 | 20 | ## 0.0.1 21 | 22 | Initial Commit. Yay! 23 | -------------------------------------------------------------------------------- /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 run validate` to validate you've got it working 13 | 4. Create a branch for your PR 14 | 15 | > Tip: Keep your `master` branch pointing at the original repository and make 16 | > pull requests from branches on your fork. To do this, run: 17 | > 18 | > ``` 19 | > git remote add upstream https://github.com/kentcdodds/babel-plugin-source.git 20 | > git fetch upstream 21 | > git branch --set-upstream-to=upstream/master master 22 | > ``` 23 | > 24 | > This will add the original repository as a "remote" called "upstream," 25 | > Then fetch the git information from that remote, then set your local `master` 26 | > branch to use the upstream master branch whenever you run `git pull`. 27 | > Then you can make all of your pull request branches based on this `master` 28 | > branch. Whenever you want to update your version of `master`, do a regular 29 | > `git pull`. 30 | 31 | ## Add yourself as a contributor 32 | 33 | This project follows the [all contributors][all-contributors] specification. 34 | To add yourself to the table of contributors on the `README.md`, please use the 35 | automated script as part of your PR: 36 | 37 | ```console 38 | npm run add-contributor 39 | ``` 40 | 41 | Follow the prompt and commit `.all-contributorsrc` and `README.md` in the PR. 42 | If you've already added yourself to the list and are making 43 | a new type of contribution, you can run it again and select the added 44 | contribution type. 45 | 46 | ## Committing and Pushing changes 47 | 48 | Please make sure to run the tests before you commit your changes. You can run 49 | `npm run test:update` which will update any snapshots that need updating. 50 | Make sure to include those changes (if they exist) in your commit. 51 | 52 | ### opt into git hooks 53 | 54 | There are git hooks set up with this project that are automatically installed 55 | when you install dependencies. They're really handy, but are turned off by 56 | default (so as to not hinder new contributors). You can opt into these by 57 | creating a file called `.opt-in` at the root of the project and putting this 58 | inside: 59 | 60 | ``` 61 | pre-commit 62 | ``` 63 | 64 | ## Help needed 65 | 66 | Please checkout the [the open issues][issues] 67 | 68 | Also, please watch the repo and respond to questions/bug reports/feature 69 | requests! Thanks! 70 | 71 | [egghead]: https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github 72 | [all-contributors]: https://github.com/kentcdodds/all-contributors 73 | [issues]: https://github.com/kentcdodds/babel-plugin-source/issues 74 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2017 Kent C. Dodds 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 | SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

babel-plugin-source

3 | Extract source code to strings at build-time 4 |
5 | 6 |
7 | 8 | [![Build Status][build-badge]][build] 9 | [![Code Coverage][coverage-badge]][coverage] 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-2-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 | [![Examples][examples-badge]][examples] 19 | 20 | [![Star on GitHub][github-star-badge]][github-star] 21 | [![Watch on GitHub][github-watch-badge]][github-watch] 22 | [![Tweet][twitter-badge]][twitter] 23 | 24 | ## The problem 25 | 26 | You are writing code that needs to both function and be displayed as raw text. You shouldn't have to write and maintain the code snippet twice (once as real code, and again as a template string). 27 | 28 | ## This solution 29 | 30 | `babel-plugin-source` allows you to extract arbitrary code blocks into string variables within the file they are located. This allows you to write the code once, then utilize it's raw text immediately in the same file. You can display it, log it, export it, etc. 31 | 32 | **Before**: 33 | 34 | ```js 35 | let MyCompSource; 36 | 37 | // @source MyCompSource 38 | const MyComp = () =>
Hello there!
; 39 | // @source MyCompSource 40 | ``` 41 | 42 | **After**: 43 | 44 | ```javascript 45 | let MyCompSource = `const MyComp = () =>
Hello there!
`; 46 | 47 | const MyComp = () =>
Hello there!
; 48 | ``` 49 | 50 | ## Table of Contents 51 | 52 | 53 | 54 | 55 | 56 | * [Installation](#installation) 57 | * [Usage](#usage) 58 | * [Configure with Babel](#configure-with-babel) 59 | * [Via `.babelrc` (Recommended)](#via-babelrc-recommended) 60 | * [Via CLI](#via-cli) 61 | * [Via Node API](#via-node-api) 62 | * [Examples](#examples) 63 | * [FAQ](#faq) 64 | * [How is this different from [webpack][webpack] [loaders][webpack-loaders]?](#how-is-this-different-from-webpackwebpack-loaderswebpack-loaders) 65 | * [Other Solutions](#other-solutions) 66 | * [Contributors](#contributors) 67 | * [LICENSE](#license) 68 | 69 | 70 | 71 | ## Installation 72 | 73 | This module is distributed via [npm][npm] which is bundled with [node][node] and 74 | should be installed as one of your project's `devDependencies`: 75 | 76 | ``` 77 | npm install --save-dev babel-plugin-source 78 | ``` 79 | 80 | ## Usage 81 | 82 | **Before**: 83 | 84 | ```javascript 85 | let MyCompSource; 86 | 87 | // @source MyCompSource 88 | const MyComp = () =>
Hello there!
; 89 | // @Source 90 | ``` 91 | 92 | **After**: 93 | 94 | ```javascript 95 | let MyCompSource = `const MyComp = () =>
Hello there!
`; 96 | 97 | const MyComp = () =>
Hello there!
; 98 | ``` 99 | 100 | ## Configure with Babel 101 | 102 | ### Via `.babelrc` (Recommended) 103 | 104 | **.babelrc** 105 | 106 | ```json 107 | { 108 | "plugins": ["source"] 109 | } 110 | ``` 111 | 112 | ### Via CLI 113 | 114 | ```sh 115 | babel --plugins source script.js 116 | ``` 117 | 118 | ### Via Node API 119 | 120 | ```javascript 121 | require("babel-core").transform("code", { 122 | plugins: ["source"] 123 | }); 124 | ``` 125 | 126 | ## Examples 127 | 128 | * [React-Charts.js.org](https:react-charts.js.org) - Source code examples 129 | 130 | ## FAQ 131 | 132 | ### How is this different from [webpack][webpack] [loaders][webpack-loaders]? 133 | 134 | This plugin was inspired by webpack's [raw-loader][raw-loader]. The benefit of 135 | using this over that loader (or any other loader) is that it allows more fine-grained 136 | control over the extraction process and integrates tightlyo with 137 | your existing babel pipeline. This is also extremely useful if 138 | you aren't bundling your code with `webpack`, but still using 139 | babel. 140 | 141 | 145 | 146 | ## Other Solutions 147 | 148 | I'm not aware of any, so if you are, please [make a pull request][prs] and add it 149 | here! 150 | 151 | ## Contributors 152 | 153 | Thanks goes to these people ([emoji key][emojis]): 154 | 155 | 156 | 157 | 158 | | [
Tanner Linsley](http://Nozzle.io)
[💻](https://github.com/tannerlinsley/babel-plugin-sourcer/commits?author=tannerlinsley "Code") [🎨](#design-tannerlinsley "Design") [📖](https://github.com/tannerlinsley/babel-plugin-sourcer/commits?author=tannerlinsley "Documentation") [💡](#example-tannerlinsley "Examples") | [
Kent C. Dodds](https://kentcdodds.com)
[💬](#question-kentcdodds "Answering Questions") [💻](https://github.com/tannerlinsley/babel-plugin-sourcer/commits?author=kentcdodds "Code") [🤔](#ideas-kentcdodds "Ideas, Planning, & Feedback") [🔌](#plugin-kentcdodds "Plugin/utility libraries") [📢](#talk-kentcdodds "Talks") [🔧](#tool-kentcdodds "Tools") | 159 | | :---: | :---: | 160 | 161 | 162 | 163 | This project follows the [all-contributors][all-contributors] specification. 164 | Contributions of any kind welcome! 165 | 166 | ## LICENSE 167 | 168 | MIT 169 | 170 | [npm]: https://www.npmjs.com/ 171 | [node]: https://nodejs.org 172 | [build-badge]: https://img.shields.io/travis/tannerlinsley/babel-plugin-source.svg?style=flat-square 173 | [build]: https://travis-ci.org/tannerlinsley/babel-plugin-source 174 | [coverage-badge]: https://img.shields.io/codecov/c/github/tannerlinsley/babel-plugin-source.svg?style=flat-square 175 | [coverage]: https://codecov.io/github/tannerlinsley/babel-plugin-source 176 | [version-badge]: https://img.shields.io/npm/v/babel-plugin-source.svg?style=flat-square 177 | [package]: https://www.npmjs.com/package/babel-plugin-source 178 | [downloads-badge]: https://img.shields.io/npm/dm/babel-plugin-source.svg?style=flat-square 179 | [npm-stat]: http://npm-stat.com/charts.html?package=babel-plugin-source&from=2016-04-01 180 | [license-badge]: https://img.shields.io/npm/l/babel-plugin-source.svg?style=flat-square 181 | [license]: https://github.com/tannerlinsley/babel-plugin-source/blob/master/LICENSE 182 | [prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square 183 | [prs]: http://makeapullrequest.com 184 | [donate-badge]: https://img.shields.io/badge/$-support-green.svg?style=flat-square 185 | [donate]: http://kcd.im/donate 186 | [coc-badge]: https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square 187 | [coc]: https://github.com/tannerlinsley/babel-plugin-source/blob/master/other/CODE_OF_CONDUCT.md 188 | [examples-badge]: https://img.shields.io/badge/%F0%9F%92%A1-examples-8C8E93.svg?style=flat-square 189 | [examples]: https://github.com/tannerlinsley/babel-plugin-source/blob/master/other/EXAMPLES.md 190 | [github-watch-badge]: https://img.shields.io/github/watchers/tannerlinsley/babel-plugin-source.svg?style=social 191 | [github-watch]: https://github.com/tannerlinsley/babel-plugin-source/watchers 192 | [github-star-badge]: https://img.shields.io/github/stars/tannerlinsley/babel-plugin-source.svg?style=social 193 | [github-star]: https://github.com/tannerlinsley/babel-plugin-source/stargazers 194 | [twitter]: https://twitter.com/intent/tweet?text=Check%20out%20babel-plugin-source!%20https://github.com/tannerlinsley/babel-plugin-source%20%F0%9F%91%8D 195 | [twitter-badge]: https://img.shields.io/twitter/url/https/github.com/tannerlinsley/babel-plugin-source.svg?style=social 196 | [emojis]: https://github.com/kentcdodds/all-contributors#emoji-key 197 | [all-contributors]: https://github.com/kentcdodds/all-contributors 198 | [prepack]: https://github.com/facebook/prepack 199 | 200 | 201 | 202 | [webpack]: https://webpack.js.org/ 203 | [webpack-loaders]: https://webpack.js.org/concepts/loaders/ 204 | [val-loader]: https://github.com/webpack-contrib/val-loader 205 | -------------------------------------------------------------------------------- /macro.js: -------------------------------------------------------------------------------- 1 | // this is here to make the import/require API nicer: 2 | // import source from 'babel-plugin-source/macro' 3 | module.exports = require('./dist/macro') 4 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "babel-plugin-source", 3 | "version": "0.0.6", 4 | "description": "Extract source code to strings at build-time", 5 | "engines": { 6 | "node": ">=4", 7 | "npm": ">=3" 8 | }, 9 | "main": "dist/index.js", 10 | "scripts": { 11 | "add-contributor": "kcd-scripts contributors add", 12 | "build": "kcd-scripts build", 13 | "lint": "eslint src", 14 | "test": "kcd-scripts test", 15 | "test:update": "npm test -- --updateSnapshot --coverage", 16 | "validate": "kcd-scripts validate", 17 | "precommit": "yarn validate" 18 | }, 19 | "files": [ 20 | "dist", 21 | "macro.js" 22 | ], 23 | "keywords": [ 24 | "babel", 25 | "babel-plugin", 26 | "source", 27 | "code", 28 | "babel-plugin-macros" 29 | ], 30 | "author": "Tanner Linsley ", 31 | "license": "MIT", 32 | "dependencies": { 33 | "@babel/runtime": "^7.4.5", 34 | "babel-plugin-macros": "^2.0.0", 35 | "babel-register": "^6.26.0", 36 | "babylon": "^6.18.0", 37 | "eslint-config-prettier": "^4.3.0", 38 | "require-from-string": "^2.0.1" 39 | }, 40 | "devDependencies": { 41 | "@babel/preset-react": "^7.0.0", 42 | "ast-pretty-print": "^2.0.1", 43 | "babel-eslint": "9.x", 44 | "babel-plugin-tester": "^6.2.1", 45 | "eslint": "5.x", 46 | "eslint-config-react-app": "^4.0.1", 47 | "eslint-plugin-flowtype": "2.x", 48 | "eslint-plugin-import": "2.x", 49 | "eslint-plugin-jsx-a11y": "6.x", 50 | "eslint-plugin-react": "7.x", 51 | "eslint-plugin-react-hooks": "1.5.0", 52 | "kcd-scripts": "^1.4.0" 53 | }, 54 | "eslintConfig": { 55 | "extends": [ 56 | "react-app", 57 | "prettier" 58 | ] 59 | }, 60 | "eslintIgnore": [ 61 | "node_modules", 62 | "coverage", 63 | "dist" 64 | ], 65 | "babel": { 66 | "presets": [ 67 | "kcd-scripts/babel" 68 | ] 69 | }, 70 | "repository": { 71 | "type": "git", 72 | "url": "https://github.com/tannerlinsley/babel-plugin-source.git" 73 | }, 74 | "bugs": { 75 | "url": "https://github.com/tannerlinsley/babel-plugin-source/issues" 76 | }, 77 | "homepage": "https://github.com/tannerlinsley/babel-plugin-source#readme" 78 | } 79 | -------------------------------------------------------------------------------- /src/__tests__/__snapshots__/index.js.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`source 1. source: 1. source 1`] = ` 4 | 5 | let MySource 6 | function ScopedFunction () { 7 | /* @source MySource */ const foo = 'bar' /* @source MySource */ 8 | } 9 | 10 | ↓ ↓ ↓ ↓ ↓ ↓ 11 | 12 | let MySource = "const foo = 'bar' "; 13 | 14 | function ScopedFunction() { 15 | /* @source MySource */ 16 | const foo = 'bar'; 17 | /* @source MySource */ 18 | } 19 | 20 | `; 21 | 22 | exports[`source 2. source: 2. source 1`] = ` 23 | 24 | function ScopedFunction () { 25 | let MySource 26 | /* @source MySource */ const foo = 'bar' /* @source MySource */ 27 | } 28 | 29 | ↓ ↓ ↓ ↓ ↓ ↓ 30 | 31 | function ScopedFunction() { 32 | let MySource = "const foo = 'bar' "; 33 | /* @source MySource */ 34 | 35 | const foo = 'bar'; 36 | /* @source MySource */ 37 | } 38 | 39 | `; 40 | 41 | exports[`source 3. source: 3. source 1`] = ` 42 | 43 | let MySource 44 | /* @source MySource */ const foo = 'bar' /* @source MySource */ 45 | 46 | ↓ ↓ ↓ ↓ ↓ ↓ 47 | 48 | let MySource = "const foo = 'bar' "; 49 | /* @source MySource */ 50 | 51 | const foo = 'bar'; 52 | /* @source MySource */ 53 | 54 | `; 55 | 56 | exports[`source 4. source: 4. source 1`] = ` 57 | 58 | let MySource 59 | /* @source MySource */ const foo = 'bar' /* @source MySource */ 60 | 61 | ↓ ↓ ↓ ↓ ↓ ↓ 62 | 63 | let MySource = "const foo = 'bar' "; 64 | /* @source MySource */ 65 | 66 | const foo = 'bar'; 67 | /* @source MySource */ 68 | 69 | `; 70 | 71 | exports[`source 5. source: 5. source 1`] = ` 72 | 73 | let MySource 74 | // @source MySource 75 | const MyComponent = () => ( 76 | console.log('Hello!') 77 | ) 78 | // @source MySource 79 | 80 | ↓ ↓ ↓ ↓ ↓ ↓ 81 | 82 | let MySource = "const MyComponent = () => (\\n console.log('Hello!')\\n)"; // @source MySource 83 | 84 | const MyComponent = () => console.log('Hello!'); // @source MySource 85 | 86 | `; 87 | 88 | exports[`source 6. source: 6. source 1`] = ` 89 | 90 | let MySource 91 | // @source MySource 92 | const MyComponent = () => ( 93 | console.log('Hello!') 94 | ) 95 | // @source MySource 96 | 97 | ↓ ↓ ↓ ↓ ↓ ↓ 98 | 99 | let MySource = "const MyComponent = () => (\\n console.log('Hello!')\\n)"; // @source MySource 100 | 101 | const MyComponent = () => console.log('Hello!'); // @source MySource 102 | 103 | `; 104 | 105 | exports[`source 7. source: 7. source 1`] = ` 106 | 107 | let MySource 108 | let MyOtherSource 109 | // @source MySource 110 | const MyComponent = () => ( 111 | console.log('Hello!') 112 | ) 113 | // @source MySource 114 | // @source MyOtherSource 115 | const MyComponent2 = () => ( 116 | console.log('Hello 2!') 117 | ) 118 | // @source MyOtherSource 119 | 120 | ↓ ↓ ↓ ↓ ↓ ↓ 121 | 122 | let MySource = "const MyComponent = () => (\\n console.log('Hello!')\\n)"; 123 | let MyOtherSource = "const MyComponent2 = () => (\\n console.log('Hello 2!')\\n)"; // @source MySource 124 | 125 | const MyComponent = () => console.log('Hello!'); // @source MySource 126 | // @source MyOtherSource 127 | 128 | 129 | const MyComponent2 = () => console.log('Hello 2!'); // @source MyOtherSource 130 | 131 | `; 132 | 133 | exports[`source 8. source: 8. source 1`] = ` 134 | 135 | let MySource 136 | // @source MySource 137 | // @source MySource 138 | 139 | ↓ ↓ ↓ ↓ ↓ ↓ 140 | 141 | let MySource = ""; // @source MySource 142 | // @source MySource 143 | 144 | `; 145 | 146 | exports[`source 9. source: 9. source 1`] = ` 147 | 148 | let MySource 149 | // @source MySource 150 | // @source MySource 151 | 152 | ↓ ↓ ↓ ↓ ↓ ↓ 153 | 154 | let MySource = ""; // @source MySource 155 | // @source MySource 156 | 157 | `; 158 | 159 | exports[`source 10. source: 10. source 1`] = ` 160 | 161 | let MySource = 'I should be the here' 162 | /* @source */ const foo = 'bar' /* @source */ 163 | 164 | ↓ ↓ ↓ ↓ ↓ ↓ 165 | 166 | let MySource = 'I should be the here'; 167 | /* @source */ 168 | 169 | const foo = 'bar'; 170 | /* @source */ 171 | 172 | `; 173 | 174 | exports[`source 11. source: 11. source 1`] = ` 175 | 176 | let MySource = 'I should be the here' 177 | /* other comment */ const foo = 'bar' /* other comment */ 178 | 179 | ↓ ↓ ↓ ↓ ↓ ↓ 180 | 181 | let MySource = 'I should be the here'; 182 | /* other comment */ 183 | 184 | const foo = 'bar'; 185 | /* other comment */ 186 | 187 | `; 188 | 189 | exports[`source 12. source: 12. source 1`] = ` 190 | 191 | /* other comment */ const foo = 'bar' /* other comment */ 192 | 193 | ↓ ↓ ↓ ↓ ↓ ↓ 194 | 195 | /* other comment */ 196 | const foo = 'bar'; 197 | /* other comment */ 198 | 199 | `; 200 | 201 | exports[`source 13. source: 13. source 1`] = ` 202 | 203 | let MySources 204 | let MySource2 205 | let MySource3 206 | let MySource4 207 | 208 | /* @source MySources */ 209 | 210 | if (true) { 211 | // do this 212 | } 213 | const MyComponent = () => ( 214 | /* @source MySource2 */ 215 | console.log('Hello!') 216 | /* @source MySource2 */ 217 | 218 | ) 219 | const AnotherComponent = () => ( 220 | // @source MySource3 221 | console.log('Hello Again!') 222 | // @source MySource3 223 | ) 224 | 225 | /* @source MySource4 */ const aVariable = 'Hello!' /* @source MySource4 */ 226 | 227 | /* @source MySources */ 228 | 229 | const noSource = 'please!' 230 | 231 | ↓ ↓ ↓ ↓ ↓ ↓ 232 | 233 | let MySources = "if (true) {\\n // do this\\n}\\nconst MyComponent = () => (\\n /* @source MySource2 */\\n console.log('Hello!')\\n /* @source MySource2 */\\n)\\nconst AnotherComponent = () => (\\n // @source MySource3\\n console.log('Hello Again!')\\n // @source MySource3\\n)\\n/* @source MySource4 */ const aVariable = 'Hello!' /* @source MySource4 */"; 234 | let MySource2 = "console.log('Hello!')"; 235 | let MySource3 = "console.log('Hello Again!')"; 236 | let MySource4 = "const aVariable = 'Hello!' "; 237 | /* @source MySources */ 238 | 239 | if (true) {// do this 240 | } 241 | 242 | const MyComponent = () => console.log('Hello!'); 243 | 244 | const AnotherComponent = () => console.log('Hello Again!'); 245 | /* @source MySource4 */ 246 | 247 | 248 | const aVariable = 'Hello!'; 249 | /* @source MySource4 */ 250 | 251 | /* @source MySources */ 252 | 253 | const noSource = 'please!'; 254 | 255 | `; 256 | 257 | exports[`source 14. source: 14. source 1`] = ` 258 | 259 | let MySource 260 | const MyComponent = () => ( 261 | // @source MySource 262 | console.log('Hello!') 263 | // @source MySource 264 | ) 265 | 266 | ↓ ↓ ↓ ↓ ↓ ↓ 267 | 268 | let MySource = "console.log('Hello!')"; 269 | 270 | const MyComponent = () => console.log('Hello!'); 271 | 272 | `; 273 | 274 | exports[`source 15. source: 15. source 1`] = ` 275 | 276 | const MySource = '' 277 | 278 | const MyComponent = () => ( 279 | // @source MySource 280 | console.log('Hello!') 281 | // @source MySource 282 | ) 283 | 284 | ↓ ↓ ↓ ↓ ↓ ↓ 285 | 286 | const MySource = "console.log('Hello!')"; 287 | 288 | const MyComponent = () => console.log('Hello!'); 289 | 290 | `; 291 | 292 | exports[`source 16. source: 16. source 1`] = ` 293 | 294 | let MyJSXSource 295 | const MyComponent = () => ( 296 | // @source MyJSXSource 297 |
Hello!
298 | // @source MyJSXSource 299 | ) 300 | 301 | ↓ ↓ ↓ ↓ ↓ ↓ 302 | 303 | let MyJSXSource = "
Hello!
"; 304 | 305 | const MyComponent = () => React.createElement("div", null, "Hello!"); 306 | 307 | `; 308 | 309 | exports[`source 17. source: 17. source 1`] = ` 310 | 311 | let sourceCode 312 | 313 | console.log(sourceCode) 314 | 315 | export default () => ( 316 | 317 | 318 | {({ data }) => ( 319 | // @source sourceCode 320 | 321 | 322 | 323 | 324 | 325 | 326 | // @source sourceCode 327 | )} 328 | 329 | 330 | 331 | ) 332 | 333 | ↓ ↓ ↓ ↓ ↓ ↓ 334 | 335 | let sourceCode = "\\n \\n \\n \\n \\n"; 336 | console.log(sourceCode); 337 | export default (() => React.createElement(Sidebar, null, React.createElement(ChartConfig, { 338 | dataType: "time" 339 | }, ({ 340 | data 341 | }) => React.createElement(Chart, { 342 | data: data 343 | }, React.createElement(Axis, { 344 | primary: true, 345 | type: "time", 346 | position: "bottom" 347 | }), React.createElement(Axis, { 348 | type: "linear", 349 | position: "left", 350 | stacked: true 351 | }), React.createElement(Series, { 352 | type: Area 353 | }), React.createElement(Tooltip, null))), React.createElement(Code, { 354 | source: sourceCode 355 | }))); 356 | 357 | `; 358 | 359 | exports[`source 18. source: 18. source 1`] = ` 360 | 361 | import React from 'react' 362 | 363 | // 364 | 365 | import Sidebar from 'components/Sidebar' 366 | import ChartConfig from 'components/ChartConfig' 367 | import Code from 'components/Code' 368 | 369 | import { Chart, Axis, Series, Tooltip, Area } from '../../../src' 370 | 371 | const sourceCode = '' 372 | 373 | export default () => ( 374 | 375 | 376 | {({ data }) => ( 377 | // @source sourceCode 378 | 379 | 380 | 381 | 382 | 383 | 384 | // @source sourceCode 385 | )} 386 | 387 | 388 | 389 | ) 390 | 391 | ↓ ↓ ↓ ↓ ↓ ↓ 392 | 393 | import React from 'react'; // 394 | 395 | import Sidebar from 'components/Sidebar'; 396 | import ChartConfig from 'components/ChartConfig'; 397 | import Code from 'components/Code'; 398 | import { Chart, Axis, Series, Tooltip, Area } from '../../../src'; 399 | const sourceCode = "\\n \\n \\n \\n \\n"; 400 | export default (() => React.createElement(Sidebar, null, React.createElement(ChartConfig, { 401 | dataType: "time" 402 | }, ({ 403 | data 404 | }) => React.createElement(Chart, { 405 | data: data 406 | }, React.createElement(Axis, { 407 | primary: true, 408 | type: "time", 409 | position: "bottom" 410 | }), React.createElement(Axis, { 411 | type: "linear", 412 | position: "left", 413 | stacked: true 414 | }), React.createElement(Series, { 415 | type: Area 416 | }), React.createElement(Tooltip, null))), React.createElement(Code, { 417 | source: sourceCode 418 | }))); 419 | 420 | `; 421 | 422 | exports[`source 20. source: 20. source 1`] = ` 423 | 424 | import React from 'react' 425 | 426 | // 427 | 428 | import useChartConfig from 'hooks/useChartConfig' 429 | import Box from 'components/Box' 430 | import { Chart } from '../../../dist' 431 | 432 | let sourceCode 433 | 434 | // @source sourceCode 435 | 436 | export default () => { 437 | const { data, randomizeData } = useChartConfig({ 438 | series: 10 439 | }) 440 | const series = React.useMemo( 441 | () => ({ 442 | type: 'area' 443 | }), 444 | [] 445 | ) 446 | const axes = React.useMemo( 447 | () => [ 448 | { primary: true, position: 'bottom', type: 'time' }, 449 | { position: 'left', type: 'linear', stacked: true } 450 | ], 451 | [] 452 | ) 453 | return ( 454 | <> 455 | 456 |
457 |
458 | 459 | 460 | 461 |
462 |
463 |             {sourceCode}
464 |           
465 | 466 | ) 467 | } 468 | // @source sourceCode 469 | 470 | ↓ ↓ ↓ ↓ ↓ ↓ 471 | 472 | import React from 'react'; // 473 | 474 | import useChartConfig from 'hooks/useChartConfig'; 475 | import Box from 'components/Box'; 476 | import { Chart } from '../../../dist'; 477 | let sourceCode = "export default () => {\\n const { data, randomizeData } = useChartConfig({\\n series: 10\\n })\\n const series = React.useMemo(\\n () => ({\\n type: 'area'\\n }),\\n []\\n )\\n const axes = React.useMemo(\\n () => [\\n { primary: true, position: 'bottom', type: 'time' },\\n { position: 'left', type: 'linear', stacked: true }\\n ],\\n []\\n )\\n return (\\n <>\\n \\n
\\n
\\n \\n \\n \\n
\\n
\\n        {sourceCode}\\n      
\\n \\n )\\n}"; // @source sourceCode 478 | 479 | export default (() => { 480 | const { 481 | data, 482 | randomizeData 483 | } = useChartConfig({ 484 | series: 10 485 | }); 486 | const series = React.useMemo(() => ({ 487 | type: 'area' 488 | }), []); 489 | const axes = React.useMemo(() => [{ 490 | primary: true, 491 | position: 'bottom', 492 | type: 'time' 493 | }, { 494 | position: 'left', 495 | type: 'linear', 496 | stacked: true 497 | }], []); 498 | return React.createElement(React.Fragment, null, React.createElement("button", { 499 | onClick: randomizeData 500 | }, "Randomize Data"), React.createElement("br", null), React.createElement("br", null), React.createElement(Box, null, React.createElement(Chart, { 501 | data: data, 502 | series: series, 503 | axes: axes, 504 | tooltip: true 505 | })), React.createElement("br", null), React.createElement("pre", null, React.createElement("code", null, sourceCode))); 506 | }); // @source sourceCode 507 | 508 | `; 509 | -------------------------------------------------------------------------------- /src/__tests__/index.js: -------------------------------------------------------------------------------- 1 | /* global expect */ 2 | 3 | import path from 'path' 4 | import pluginTester from 'babel-plugin-tester' 5 | import plugin from '../' 6 | 7 | const projectRoot = path.join(__dirname, '../../') 8 | 9 | expect.addSnapshotSerializer({ 10 | print(val) { 11 | return val.split(projectRoot).join('/') 12 | }, 13 | test(val) { 14 | return typeof val === 'string' 15 | } 16 | }) 17 | 18 | pluginTester({ 19 | plugin, 20 | snapshot: true, 21 | babelOptions: { 22 | presets: ['@babel/preset-react'] 23 | }, 24 | tests: [ 25 | ` 26 | let MySource 27 | function ScopedFunction () { 28 | /* @source MySource */ const foo = 'bar' /* @source MySource */ 29 | } 30 | `, 31 | ` 32 | function ScopedFunction () { 33 | let MySource 34 | /* @source MySource */ const foo = 'bar' /* @source MySource */ 35 | } 36 | `, 37 | ` 38 | let MySource 39 | /* @source MySource */ const foo = 'bar' /* @source MySource */ 40 | `, 41 | ` 42 | let MySource 43 | /* @source MySource */ const foo = 'bar' /* @source MySource */ 44 | `, 45 | ` 46 | let MySource 47 | // @source MySource 48 | const MyComponent = () => ( 49 | console.log('Hello!') 50 | ) 51 | // @source MySource 52 | `, 53 | ` 54 | let MySource 55 | // @source MySource 56 | const MyComponent = () => ( 57 | console.log('Hello!') 58 | ) 59 | // @source MySource 60 | `, 61 | ` 62 | let MySource 63 | let MyOtherSource 64 | // @source MySource 65 | const MyComponent = () => ( 66 | console.log('Hello!') 67 | ) 68 | // @source MySource 69 | // @source MyOtherSource 70 | const MyComponent2 = () => ( 71 | console.log('Hello 2!') 72 | ) 73 | // @source MyOtherSource 74 | `, 75 | ` 76 | let MySource 77 | // @source MySource 78 | // @source MySource 79 | `, 80 | ` 81 | let MySource 82 | // @source MySource 83 | // @source MySource 84 | `, 85 | ` 86 | let MySource = 'I should be the here' 87 | /* @source */ const foo = 'bar' /* @source */ 88 | `, 89 | ` 90 | let MySource = 'I should be the here' 91 | /* other comment */ const foo = 'bar' /* other comment */ 92 | `, 93 | ` 94 | /* other comment */ const foo = 'bar' /* other comment */ 95 | `, 96 | ` 97 | let MySources 98 | let MySource2 99 | let MySource3 100 | let MySource4 101 | 102 | /* @source MySources */ 103 | 104 | if (true) { 105 | // do this 106 | } 107 | const MyComponent = () => ( 108 | /* @source MySource2 */ 109 | console.log('Hello!') 110 | /* @source MySource2 */ 111 | 112 | ) 113 | const AnotherComponent = () => ( 114 | // @source MySource3 115 | console.log('Hello Again!') 116 | // @source MySource3 117 | ) 118 | 119 | /* @source MySource4 */ const aVariable = 'Hello!' /* @source MySource4 */ 120 | 121 | /* @source MySources */ 122 | 123 | const noSource = 'please!' 124 | `, 125 | ` 126 | let MySource 127 | const MyComponent = () => ( 128 | // @source MySource 129 | console.log('Hello!') 130 | // @source MySource 131 | ) 132 | `, 133 | ` 134 | const MySource = '' 135 | 136 | const MyComponent = () => ( 137 | // @source MySource 138 | console.log('Hello!') 139 | // @source MySource 140 | ) 141 | `, 142 | ` 143 | let MyJSXSource 144 | const MyComponent = () => ( 145 | // @source MyJSXSource 146 |
Hello!
147 | // @source MyJSXSource 148 | ) 149 | `, 150 | ` 151 | let sourceCode 152 | 153 | console.log(sourceCode) 154 | 155 | export default () => ( 156 | 157 | 158 | {({ data }) => ( 159 | // @source sourceCode 160 | 161 | 162 | 163 | 164 | 165 | 166 | // @source sourceCode 167 | )} 168 | 169 | 170 | 171 | ) 172 | `, 173 | `import React from 'react' 174 | 175 | // 176 | 177 | import Sidebar from 'components/Sidebar' 178 | import ChartConfig from 'components/ChartConfig' 179 | import Code from 'components/Code' 180 | 181 | import { Chart, Axis, Series, Tooltip, Area } from '../../../src' 182 | 183 | const sourceCode = '' 184 | 185 | export default () => ( 186 | 187 | 188 | {({ data }) => ( 189 | // @source sourceCode 190 | 191 | 192 | 193 | 194 | 195 | 196 | // @source sourceCode 197 | )} 198 | 199 | 200 | 201 | ) 202 | `, 203 | { 204 | error: true, 205 | snapshot: false, 206 | code: ` 207 | const MyComponent = () => ( 208 | // @source MySource 209 | console.log('Hello!') 210 | // @source MySource 211 | ) 212 | ` 213 | }, 214 | `import React from 'react' 215 | 216 | // 217 | 218 | import useChartConfig from 'hooks/useChartConfig' 219 | import Box from 'components/Box' 220 | import { Chart } from '../../../dist' 221 | 222 | let sourceCode 223 | 224 | // @source sourceCode 225 | 226 | export default () => { 227 | const { data, randomizeData } = useChartConfig({ 228 | series: 10 229 | }) 230 | const series = React.useMemo( 231 | () => ({ 232 | type: 'area' 233 | }), 234 | [] 235 | ) 236 | const axes = React.useMemo( 237 | () => [ 238 | { primary: true, position: 'bottom', type: 'time' }, 239 | { position: 'left', type: 'linear', stacked: true } 240 | ], 241 | [] 242 | ) 243 | return ( 244 | <> 245 | 246 |
247 |
248 | 249 | 250 | 251 |
252 |
253 |             {sourceCode}
254 |           
255 | 256 | ) 257 | } 258 | // @source sourceCode 259 | ` 260 | ] 261 | }) 262 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | export default function sourcePlugin(babel) { 2 | const { types: t } = babel 3 | 4 | const expressionVisitor = { 5 | Expression(path) { 6 | const { node } = path 7 | const { leading, trailing } = getSourceNameCommentsFromNode(node) 8 | if (node.__sourceHandled || (!leading.length && !trailing.length)) { 9 | return 10 | } 11 | node.__sourceHandled = true 12 | const lastLeading = leading[leading.length - 1] 13 | const firstTrailing = trailing[trailing.length - 1] 14 | this.handleComment(lastLeading) 15 | this.handleComment(firstTrailing) 16 | removeSourceCommentsFromNode(node) 17 | } 18 | } 19 | return { 20 | name: 'source', 21 | visitor: { 22 | Scopable(path) { 23 | // Don't handle nodes twice 24 | if (path.node.__sourceHandled) { 25 | return 26 | } 27 | 28 | path.node.__sourceHandled = true 29 | const { body } = path.node 30 | const sources = {} 31 | 32 | const handleComment = comment => { 33 | if (!comment) { 34 | return 35 | } 36 | if (sources[comment.name]) { 37 | // Don't handle duplicates 38 | if (sources[comment.name].start === comment.node) { 39 | return 40 | } 41 | // Don't handle closed sources 42 | if (sources[comment.name].end) { 43 | return 44 | } 45 | // Close source 46 | sources[comment.name].end = comment.node 47 | } else { 48 | // Otherwise, open it up 49 | sources[comment.name] = { 50 | start: comment.node 51 | } 52 | } 53 | } 54 | 55 | handleComment() 56 | 57 | if (body && body.length) { 58 | body.forEach(node => { 59 | const { leading, trailing } = getSourceNameCommentsFromNode(node) 60 | ;[...leading, ...trailing].forEach(handleComment) 61 | }) 62 | } 63 | 64 | path.traverse(expressionVisitor, { handleComment }) 65 | 66 | Object.keys(sources).forEach(sourceName => { 67 | const source = sources[sourceName] 68 | const sourceCode = makeSourceCode( 69 | this.file.code, 70 | source.start, 71 | source.end 72 | ) 73 | const variablePath = path.find( 74 | path => path.scope.bindings[sourceName] 75 | ) 76 | if (!variablePath || !variablePath.scope) { 77 | throw path.buildCodeFrameError( 78 | `Couldn't find the 'let/var ${sourceName}' variable for @source ${sourceName}!` 79 | ) 80 | } 81 | variablePath.scope.bindings[ 82 | sourceName 83 | ].path.node.init = t.stringLiteral(sourceCode) 84 | removeSourceCommentsFromNode(source.start) 85 | removeSourceCommentsFromNode(source.end) 86 | }) 87 | } 88 | } 89 | } 90 | } 91 | 92 | function isSourceComment(comment) { 93 | const normalizedComment = comment.value.trim().split(' ') 94 | if (normalizedComment.length !== 2) { 95 | return 96 | } 97 | const isSource = normalizedComment[0].trim() === '@source' 98 | let hasName = normalizedComment[1] 99 | hasName = hasName && normalizedComment[1].trim() 100 | 101 | return isSource && hasName ? { name: hasName, node: comment } : false 102 | } 103 | 104 | function getSourceNameCommentsFromNode(node) { 105 | const { leadingComments, trailingComments } = node 106 | return { 107 | leading: (leadingComments || []).map(isSourceComment).filter(Boolean), 108 | trailing: (trailingComments || []).map(isSourceComment).filter(Boolean) 109 | } 110 | } 111 | 112 | function removeSourceCommentsFromNode(node) { 113 | node.leadingComments = node.leadingComments 114 | ? node.leadingComments.filter(d => !isSourceComment(d)) 115 | : node.leadingComments 116 | node.trailingComments = node.trailingComments 117 | ? node.trailingComments.filter(d => !isSourceComment(d)) 118 | : node.trailingComments 119 | return node 120 | } 121 | 122 | function makeSourceCode(source, startComment, endComment) { 123 | // console.log(startComment, endComment) 124 | const startLine = startComment.loc.end.line 125 | const endLine = endComment.loc.start.line 126 | const startColumn = startComment.loc.end.column 127 | const endColumn = endComment.loc.start.column 128 | const isSingleLine = startLine === endLine 129 | 130 | let sourceLines = source 131 | .split('\n') 132 | .slice(startLine - 1, isSingleLine ? startLine : endLine) 133 | 134 | sourceLines[0] = sourceLines[0].slice(startColumn) 135 | sourceLines[sourceLines.length - 1] = sourceLines[ 136 | sourceLines.length - 1 137 | ].slice(0, isSingleLine ? endColumn - startColumn : endColumn) 138 | sourceLines = sourceLines.filter(d => d.length) 139 | const shortestIndentation = sourceLines.reduce((acc, next) => { 140 | let length = 0 141 | while (next.charAt(length) === ' ') { 142 | length += 1 143 | } 144 | return acc < length ? acc : length 145 | }, Infinity) 146 | sourceLines = sourceLines.filter(d => d.replace(/[ ]*/, '')) 147 | return sourceLines.map(d => d.substring(shortestIndentation)).join('\n') 148 | } 149 | --------------------------------------------------------------------------------