├── .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 | [](#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 | Randomize Data
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 Randomize Data \\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 | Randomize Data
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 |
--------------------------------------------------------------------------------