babel-plugin-prototype-prop-define
Advanced tools
+3
-4
| { | ||
| "name": "babel-plugin-prototype-prop-define", | ||
| "version": "1.0.1", | ||
| "description": "Rewrite assignments on the prototypes to Object.defineProperty calls", | ||
| "version": "2.0.1", | ||
| "description": "Transform assignments to properties on built-in prototypes to Object.defineProperty calls", | ||
| "main": "src/index.js", | ||
@@ -27,3 +27,2 @@ "engines": { | ||
| "dependencies": { | ||
| "babel-plugin-macros": "^2.2.1", | ||
| "require-from-string": "^2.0.2" | ||
@@ -34,3 +33,3 @@ }, | ||
| "babel-plugin-tester": "^5.0.0", | ||
| "kcd-scripts": "^0.39.0" | ||
| "kcd-scripts": "^1.6.0" | ||
| }, | ||
@@ -37,0 +36,0 @@ "eslintConfig": { |
+22
-202
@@ -1,219 +0,39 @@ | ||
| <div align="center"> | ||
| <h1>babel-plugin-boilerplate :emoji:</h1> | ||
| <p>your next babel plugin description</p> | ||
| </div> | ||
| <hr /> | ||
| <!-- prettier-ignore-start --> | ||
| [](#contributors) | ||
| [![PRs Welcome][prs-badge]][prs] | ||
| [![Code of Conduct][coc-badge]][coc] | ||
| [](https://github.com/kentcdodds/babel-plugin-macros) | ||
| <!-- prettier-ignore-end --> | ||
| ## The problem | ||
| The problem your plugin solves | ||
| > more resources the user shoudl read | ||
| ## This solution | ||
| What this plugin does | ||
| How this plugin works | ||
| ## Table of Contents | ||
| <!-- START doctoc generated TOC please keep comment here to allow auto update --> | ||
| <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> | ||
| - [Installation](#installation) | ||
| - [Usage](#usage) | ||
| - [first usage style](#first-usage-style) | ||
| - [usage style 2](#usage-style-2) | ||
| - [Configure with Babel](#configure-with-babel) | ||
| - [Via `.babelrc` (Recommended)](#via-babelrc-recommended) | ||
| - [Via CLI](#via-cli) | ||
| - [Via Node API](#via-node-api) | ||
| - [Use with `babel-plugin-macros`](#use-with-babel-plugin-macros) | ||
| - [APIs not supported by the macro](#apis-not-supported-by-the-macro) | ||
| - [Caveats](#caveats) | ||
| - [Examples](#examples) | ||
| - [Inspiration](#inspiration) | ||
| - [Other Solutions](#other-solutions) | ||
| - [Contributors](#contributors) | ||
| - [LICENSE](#license) | ||
| - [babel-plugin-prototype-prop-define](#babel-plugin-prototype-prop-define) | ||
| - [background](#background) | ||
| <!-- END doctoc generated TOC please keep comment here to allow auto update --> | ||
| ## Installation | ||
| # babel-plugin-prototype-prop-define | ||
| This module is distributed via [npm][npm] which is bundled with [node][node] and | ||
| should be installed as one of your project's `devDependencies`: | ||
| A babel plugin for working around javascript's "override mistake" when dealing with frozen Primordials (built-ins). | ||
| ``` | ||
| npm install --save-dev babel-plugin-boilerplate | ||
| ``` | ||
| ### background | ||
| ## Usage | ||
| If your primordials are frozen, such as in [SES](https://github.com/agoric/ses), assigning keys that are found on the prototype will throw an error. | ||
| More notes on usage | ||
| ```js | ||
| Object.freeze(Object.prototype) | ||
| ### first usage style | ||
| **Before**: | ||
| ```javascript | ||
| // before | ||
| const x = {} | ||
| x.toString = () => 'hello' | ||
| // => TypeError: Cannot assign to read only property 'toString' of object '#<Object>' | ||
| ``` | ||
| **After** some notes here: | ||
| Since this is a common, this plugin is provided to transform code into a safe form using `Object.defineProperty`: | ||
| ```javascript | ||
| // after | ||
| ``` | ||
| ```js | ||
| Object.freeze(Object.prototype) | ||
| more notes here! | ||
| **Before**: | ||
| ```javascript | ||
| // before | ||
| ``` | ||
| **After** more notes here: | ||
| ```javascript | ||
| // after | ||
| ``` | ||
| ### usage style 2 | ||
| **Before**: | ||
| ```javascript | ||
| // before | ||
| ``` | ||
| **After** more notes here: | ||
| ```javascript | ||
| // after | ||
| ``` | ||
| ## Configure with Babel | ||
| ### Via `.babelrc` (Recommended) | ||
| **.babelrc** | ||
| ```json | ||
| { | ||
| "plugins": ["BOILERPLATE"] | ||
| } | ||
| ``` | ||
| ### Via CLI | ||
| ```sh | ||
| babel --plugins BOILERPLATE script.js | ||
| ``` | ||
| ### Via Node API | ||
| ```javascript | ||
| require('babel-core').transform('code', { | ||
| plugins: ['BOILERPLATE'], | ||
| const x = {} | ||
| Object.defineProperty(x, 'toString', { | ||
| value: () => 'hello', | ||
| writable: true, | ||
| enumerable: true, | ||
| configurable: true, | ||
| }) | ||
| x.toString() | ||
| // => 'hello' | ||
| ``` | ||
| ## Use with `babel-plugin-macros` | ||
| Once you've | ||
| [configured `babel-plugin-macros`](https://github.com/kentcdodds/babel-plugin-macros/blob/master/other/docs/user.md) | ||
| you can import/require the boilerplate macro at `babel-plugin-boilerplate/macro`. For | ||
| example: | ||
| ```javascript | ||
| import yourmacro from 'babel-plugin-boilerplate/macro' | ||
| // user yourmacro | ||
| ↓ ↓ ↓ ↓ ↓ ↓ | ||
| // output | ||
| ``` | ||
| ### APIs not supported by the macro | ||
| - one | ||
| - two | ||
| > You could also use [`boilerplate.macro`][boilerplate.macro] if you'd prefer to type | ||
| > less 😀 | ||
| ## Caveats | ||
| any caveats you like to say | ||
| ## Examples | ||
| - Some examples and links here | ||
| ## Inspiration | ||
| This is based on [babel-plugin-boilerplate](https://github.com/kentcdodds/babel-plugin-boilerplate). | ||
| ## Other Solutions | ||
| I'm not aware of any, if you are please [make a pull request][prs] and add it | ||
| here! | ||
| ## Contributors | ||
| Thanks goes to these people ([emoji key][emojis]): | ||
| <!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --> | ||
| <!-- prettier-ignore --> | ||
| | [<img src="https://avatars.githubusercontent.com/u/1500684?v=3" width="100px;"/><br /><sub><b>Kent C. Dodds</b></sub>](https://kentcdodds.com)<br />[💻](https://github.com/sw-yx/babel-plugin-boilerplate/commits?author=kentcdodds "Code") [📖](https://github.com/sw-yx/babel-plugin-boilerplate/commits?author=kentcdodds "Documentation") [🚇](#infra-kentcdodds "Infrastructure (Hosting, Build-Tools, etc)") [⚠️](https://github.com/sw-yx/babel-plugin-boilerplate/commits?author=kentcdodds "Tests") | [<img src="https://avatars1.githubusercontent.com/u/1958812?v=4" width="100px;"/><br /><sub><b>Michael Rawlings</b></sub>](https://github.com/mlrawlings)<br />[💻](https://github.com/sw-yx/babel-plugin-boilerplate/commits?author=mlrawlings "Code") [📖](https://github.com/sw-yx/babel-plugin-boilerplate/commits?author=mlrawlings "Documentation") [⚠️](https://github.com/sw-yx/babel-plugin-boilerplate/commits?author=mlrawlings "Tests") | [<img src="https://avatars3.githubusercontent.com/u/5230863?v=4" width="100px;"/><br /><sub><b>Jan Willem Henckel</b></sub>](https://jan.cologne)<br />[💻](https://github.com/sw-yx/babel-plugin-boilerplate/commits?author=djfarly "Code") [📖](https://github.com/sw-yx/babel-plugin-boilerplate/commits?author=djfarly "Documentation") [⚠️](https://github.com/sw-yx/babel-plugin-boilerplate/commits?author=djfarly "Tests") | [<img src="https://avatars3.githubusercontent.com/u/1824298?v=4" width="100px;"/><br /><sub><b>Karan Thakkar</b></sub>](https://twitter.com/geekykaran)<br />[📖](https://github.com/sw-yx/babel-plugin-boilerplate/commits?author=karanjthakkar "Documentation") | | ||
| | :---: | :---: | :---: | :---: | | ||
| <!-- ALL-CONTRIBUTORS-LIST:END --> | ||
| This project follows the [all-contributors][all-contributors] specification. | ||
| Contributions of any kind welcome! | ||
| ## LICENSE | ||
| MIT | ||
| <!-- prettier-ignore-start --> | ||
| [npm]: https://www.npmjs.com/ | ||
| [node]: https://nodejs.org | ||
| [build-badge]: https://img.shields.io/travis/kentcdodds/babel-plugin-boilerplate.svg?style=flat-square | ||
| [build]: https://travis-ci.org/kentcdodds/babel-plugin-boilerplate | ||
| [coverage-badge]: https://img.shields.io/codecov/c/github/kentcdodds/babel-plugin-boilerplate.svg?style=flat-square | ||
| [coverage]: https://codecov.io/github/kentcdodds/babel-plugin-boilerplate | ||
| [version-badge]: https://img.shields.io/npm/v/babel-plugin-boilerplate.svg?style=flat-square | ||
| [package]: https://www.npmjs.com/package/babel-plugin-boilerplate | ||
| [downloads-badge]: https://img.shields.io/npm/dm/babel-plugin-boilerplate.svg?style=flat-square | ||
| [npmtrends]: http://www.npmtrends.com/babel-plugin-boilerplate | ||
| [license-badge]: https://img.shields.io/npm/l/babel-plugin-boilerplate.svg?style=flat-square | ||
| [license]: https://github.com/kentcdodds/babel-plugin-boilerplate/blob/master/LICENSE | ||
| [prs-badge]: https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square | ||
| [prs]: http://makeapullrequest.com | ||
| [donate-badge]: https://img.shields.io/badge/$-support-green.svg?style=flat-square | ||
| [coc-badge]: https://img.shields.io/badge/code%20of-conduct-ff69b4.svg?style=flat-square | ||
| [coc]: https://github.com/kentcdodds/babel-plugin-boilerplate/blob/master/other/CODE_OF_CONDUCT.md | ||
| [emojis]: https://github.com/kentcdodds/all-contributors#emoji-key | ||
| [all-contributors]: https://github.com/kentcdodds/all-contributors | ||
| [glamorous]: https://github.com/paypal/glamorous | ||
| [preval]: https://github.com/kentcdodds/babel-plugin-preval | ||
| [boilerplate.macro]: https://www.npmjs.com/package/boilerplate.macro | ||
| [babel-plugin-macros]: https://github.com/kentcdodds/babel-plugin-macros | ||
| <!-- prettier-ignore-end --> |
+34
-60
@@ -21,65 +21,39 @@ import path from 'path' | ||
| tests: { | ||
| // 'does not touch non-boilerplate code': { | ||
| // snapshot: false, | ||
| // code: 'const x = notboilerplate`module.exports = "nothing"`;', | ||
| // }, | ||
| 'basic value': ` | ||
| function MyClass () {} | ||
| MyClass.prototype.toString = () => '[[MyClass]]' | ||
| 'should ignore': ` | ||
| const x = {} | ||
| x.a = () => true | ||
| `, | ||
| // 'simple variable assignment': | ||
| // 'boilerplate`module.exports = "var x = \'some directive\'"`', | ||
| // 'object with arrow function': ` | ||
| // const y = boilerplate\` | ||
| // module.exports = '({booyah: () => "booyah"})' | ||
| // \` | ||
| 'on object': ` | ||
| const x = {} | ||
| x.toString = () => true | ||
| `, | ||
| 'on prototype': ` | ||
| function MyClass () {} | ||
| MyClass.prototype.toString = () => true | ||
| `, | ||
| 'on array': ` | ||
| const x = [] | ||
| x.splice = () => true | ||
| `, | ||
| 'on function': ` | ||
| const x = () => {} | ||
| x.bind = () => true | ||
| `, | ||
| // 'key as string': ` | ||
| // const x = {} | ||
| // x["toString"] = () => true | ||
| // `, | ||
| // 'must export a string': { | ||
| // code: 'const y = boilerplate`module.exports = {}`', | ||
| // error: true, | ||
| // }, | ||
| // 'boilerplate comment': ` | ||
| // // @boilerplate | ||
| // const array = ['apple', 'orange', 'pear'] | ||
| // module.exports = array | ||
| // .map(fruit => \`export const \${fruit} = "\${fruit}";\`) | ||
| // .join('') | ||
| // `, | ||
| // 'dynamic value that is wrong': { | ||
| // code: `const x = boilerplate\`module.exports = "\${dynamic}"\``, | ||
| // error: true, | ||
| // }, | ||
| // 'import comment': 'import /* boilerplate */ "./fixtures/assign-one.js"', | ||
| // 'import comment with extra comments after': | ||
| // 'import /* boilerplate */ /* this is extra stuff */ "./fixtures/assign-one.js"', | ||
| // 'import comment with extra comments before': | ||
| // 'import /* this is extra stuff */ /* boilerplate */ "./fixtures/assign-one.js"', | ||
| // 'does not touch import comments that are irrelevant': { | ||
| // code: 'import /* this is extra stuff */"./fixtures/assign-one.js";', | ||
| // snapshot: false, | ||
| // }, | ||
| }, | ||
| }) | ||
| // // This is for any of the exta tests. We give these a name. | ||
| // pluginTester({ | ||
| // plugin, | ||
| // snapshot: true, | ||
| // babelOptions: {filename: __filename}, | ||
| // tests: { | ||
| // 'handles some dynamic values': ` | ||
| // const three = 3 | ||
| // const x = boilerplate\`module.exports = "\${three}"\` | ||
| // `, | ||
| // 'accepts babels parser options for generated code': { | ||
| // babelOptions: { | ||
| // filename: __filename, | ||
| // parserOpts: {plugins: ['flow', 'doExpressions']}, | ||
| // }, | ||
| // code: ` | ||
| // // @boilerplate | ||
| // module.exports = "var fNum: number = do { if(true) {100} else {200} };" | ||
| // `, | ||
| // }, | ||
| // }, | ||
| // }) |
+129
-77
@@ -1,8 +0,56 @@ | ||
| // const getReplacers = require('./replace') | ||
| // const {looksLike} = require('./helpers') | ||
| const primordialKeySet = new Set([ | ||
| ...Object.getOwnPropertyNames(Object.prototype), | ||
| ...Object.getOwnPropertyNames(Array.prototype), | ||
| ...Object.getOwnPropertyNames(Function.prototype), | ||
| ...Object.getOwnPropertyNames(Error.prototype), | ||
| ]) | ||
| // Set { | ||
| // 'constructor', | ||
| // '__defineGetter__', | ||
| // '__defineSetter__', | ||
| // 'hasOwnProperty', | ||
| // '__lookupGetter__', | ||
| // '__lookupSetter__', | ||
| // 'isPrototypeOf', | ||
| // 'propertyIsEnumerable', | ||
| // 'toString', | ||
| // 'valueOf', | ||
| // '__proto__', | ||
| // 'toLocaleString', | ||
| // 'length', | ||
| // 'concat', | ||
| // 'find', | ||
| // 'findIndex', | ||
| // 'pop', | ||
| // 'push', | ||
| // 'shift', | ||
| // 'unshift', | ||
| // 'slice', | ||
| // 'splice', | ||
| // 'includes', | ||
| // 'indexOf', | ||
| // 'keys', | ||
| // 'entries', | ||
| // 'forEach', | ||
| // 'filter', | ||
| // 'map', | ||
| // 'every', | ||
| // 'some', | ||
| // 'reduce', | ||
| // 'reduceRight', | ||
| // 'join', | ||
| // 'reverse', | ||
| // 'sort', | ||
| // 'lastIndexOf', | ||
| // 'copyWithin', | ||
| // 'fill', | ||
| // 'values', | ||
| // 'name', | ||
| // 'arguments', | ||
| // 'caller', | ||
| // 'apply', | ||
| // 'bind', | ||
| // 'call', | ||
| // 'message' } | ||
| // TODO: WRITE BABEL PLUGIN | ||
| // TODO: ??? | ||
| // TODO: PROFIT | ||
| module.exports = () => | ||
@@ -24,3 +72,2 @@ // { | ||
| AssignmentExpression(path) { | ||
| // | ||
@@ -30,5 +77,5 @@ // match | ||
| // node -> MyClass.prototype.toString = function(){ return "hello" } | ||
| const { node } = path | ||
| // member -> MyClass.prototype.toString | ||
| // node -> "Xyz.toString = function(){ return "hello" }" | ||
| const {node} = path | ||
| // member -> "Xyz.toString" | ||
| const member = node.left | ||
@@ -39,10 +86,10 @@ // ensure assigning to a non-computed member | ||
| // ensure member is a member expression for "prototype" | ||
| // assignmentParent -> MyClass.prototype | ||
| // assignmentParent -> "Xyz" | ||
| const assignmentParent = member.object | ||
| if (assignmentParent.type !== 'MemberExpression') return | ||
| if (assignmentParent.computed) return | ||
| // assignmentTarget -> prototype | ||
| const assignmentTarget = assignmentParent.property | ||
| if (assignmentTarget.type !== 'Identifier') return | ||
| if (assignmentTarget.name !== 'prototype') return | ||
| // assignmentParent -> "toString" | ||
| const assignmentProperty = member.property | ||
| if (assignmentProperty.type !== 'Identifier') return | ||
| if (!primordialKeySet.has(assignmentProperty.name)) return | ||
| // assignmentValue -> "function(){ return "hello" }" | ||
| const assignmentValue = node.right | ||
@@ -53,13 +100,15 @@ // | ||
| // parentStatement -> MyClass.prototype | ||
| // parentStatement -> "Xyz" | ||
| const parentStatement = assignmentParent | ||
| // propertyKeyStatement -> toString | ||
| const propertyKeyStatement = memberPropToDefinePropKeyArg(member.property) | ||
| // valueStatement -> function(){ return "hello" } | ||
| const valueStatement = node.right | ||
| // propertyKeyStatement -> "toString" | ||
| const propertyKeyStatement = memberPropToDefinePropKeyArg( | ||
| assignmentProperty, | ||
| ) | ||
| // valueStatement -> "function(){ return "hello" }" | ||
| const valueStatement = assignmentValue | ||
| const definePropertyExpression = createDefinePropertyExpression( | ||
| parentStatement, | ||
| propertyKeyStatement, | ||
| valueStatement | ||
| valueStatement, | ||
| ) | ||
@@ -73,81 +122,84 @@ | ||
| function createDefinePropertyExpression (parentStatement, propertyKeyStatement, valueStatement) { | ||
| function createDefinePropertyExpression( | ||
| parentStatement, | ||
| propertyKeyStatement, | ||
| valueStatement, | ||
| ) { | ||
| return { | ||
| "type": "CallExpression", | ||
| "callee": { | ||
| "type": "MemberExpression", | ||
| "object": { | ||
| "type": "Identifier", | ||
| "name": "Object" | ||
| type: 'CallExpression', | ||
| callee: { | ||
| type: 'MemberExpression', | ||
| object: { | ||
| type: 'Identifier', | ||
| name: 'Object', | ||
| }, | ||
| "property": { | ||
| "type": "Identifier", | ||
| "name": "defineProperty" | ||
| property: { | ||
| type: 'Identifier', | ||
| name: 'defineProperty', | ||
| }, | ||
| "computed": false | ||
| computed: false, | ||
| }, | ||
| "arguments": [ | ||
| arguments: [ | ||
| parentStatement, | ||
| propertyKeyStatement, | ||
| createPropertyDescriptor(valueStatement) | ||
| ] | ||
| createPropertyDescriptor(valueStatement), | ||
| ], | ||
| } | ||
| } | ||
| function memberPropToDefinePropKeyArg (memberProp) { | ||
| function memberPropToDefinePropKeyArg(memberProp) { | ||
| return { | ||
| "type": "StringLiteral", | ||
| "value": memberProp.name, | ||
| type: 'StringLiteral', | ||
| value: memberProp.name, | ||
| } | ||
| } | ||
| function createPropertyDescriptor (valueStatement) { | ||
| function createPropertyDescriptor(valueStatement) { | ||
| return { | ||
| "type": "ObjectExpression", | ||
| type: 'ObjectExpression', | ||
| // { value: 1, writable: true, enumerable: true, configurable: true } | ||
| "properties": [ | ||
| properties: [ | ||
| { | ||
| "type": "ObjectProperty", | ||
| "key": { | ||
| "type": "Identifier", | ||
| "name": "value" | ||
| type: 'ObjectProperty', | ||
| key: { | ||
| type: 'Identifier', | ||
| name: 'value', | ||
| }, | ||
| "value": valueStatement | ||
| value: valueStatement, | ||
| }, | ||
| { | ||
| "type": "ObjectProperty", | ||
| "key": { | ||
| "type": "Identifier", | ||
| "name": "writable" | ||
| type: 'ObjectProperty', | ||
| key: { | ||
| type: 'Identifier', | ||
| name: 'writable', | ||
| }, | ||
| "value": { | ||
| "type": "BooleanLiteral", | ||
| "value": true | ||
| } | ||
| value: { | ||
| type: 'BooleanLiteral', | ||
| value: true, | ||
| }, | ||
| }, | ||
| { | ||
| "type": "ObjectProperty", | ||
| "key": { | ||
| "type": "Identifier", | ||
| "name": "enumerable" | ||
| type: 'ObjectProperty', | ||
| key: { | ||
| type: 'Identifier', | ||
| name: 'enumerable', | ||
| }, | ||
| "value": { | ||
| "type": "BooleanLiteral", | ||
| "value": true | ||
| } | ||
| value: { | ||
| type: 'BooleanLiteral', | ||
| value: true, | ||
| }, | ||
| }, | ||
| { | ||
| "type": "ObjectProperty", | ||
| "key": { | ||
| "type": "Identifier", | ||
| "name": "configurable" | ||
| type: 'ObjectProperty', | ||
| key: { | ||
| type: 'Identifier', | ||
| name: 'configurable', | ||
| }, | ||
| "value": { | ||
| "type": "BooleanLiteral", | ||
| "value": true | ||
| } | ||
| value: { | ||
| type: 'BooleanLiteral', | ||
| value: true, | ||
| }, | ||
| }, | ||
| ] | ||
| } | ||
| } | ||
| ], | ||
| } | ||
| } |
-3
| // this is here to make the import/require API nicer: | ||
| // import boilerplate from 'babel-plugin-boilerplate/macro' | ||
| module.exports = require('./dist/macro') |
Sorry, the diff of this file is not supported yet
| import path from 'path' | ||
| import pluginTester from 'babel-plugin-tester' | ||
| import plugin from 'babel-plugin-macros' | ||
| const projectRoot = path.join(__dirname, '../../') | ||
| expect.addSnapshotSerializer({ | ||
| print(val) { | ||
| return val.split(projectRoot).join('<PROJECT_ROOT>/') | ||
| }, | ||
| test(val) { | ||
| return typeof val === 'string' | ||
| }, | ||
| }) | ||
| pluginTester({ | ||
| plugin, | ||
| snapshot: true, | ||
| babelOptions: {filename: __filename, parserOpts: {plugins: ['jsx']}}, | ||
| tests: { | ||
| 'as tag': ` | ||
| import boilerplate from '../macro' | ||
| const greeting = 'Hello world!' | ||
| boilerplate\`module.exports = "module.exports = '\${greeting}';"\` | ||
| `, | ||
| // 'as function': ` | ||
| // const myCodgen = require('../macro') | ||
| // myCodgen(\` | ||
| // module.exports = "var x = {booyah() { return 'booyah!'; } };" | ||
| // \`) | ||
| // `, | ||
| // 'as jsx': ` | ||
| // const boilerplate = require('../macro') | ||
| // const ui = ( | ||
| // <boilerplate>{"module.exports = '<div>Hi</div>'"}</boilerplate> | ||
| // ) | ||
| // `, | ||
| // 'as jsx with tag': ` | ||
| // const boilerplate = require('../macro') | ||
| // const ui = ( | ||
| // <boilerplate>{\`module.exports = '<div>Hi</div>'\`}</boilerplate> | ||
| // ) | ||
| // `, | ||
| // 'with multiple': ` | ||
| // import boilerplate from '../macro' | ||
| // boilerplate\`module.exports = ['a', 'b', 'c'].map(l => 'export const ' + l + ' = ' + JSON.stringify(l)).join(';')\` | ||
| // `, | ||
| // 'as require call': ` | ||
| // import boilerplate from '../macro'; | ||
| // var x = boilerplate.require('./fixtures/return-one'); | ||
| // `, | ||
| // 'invalid usage: as fn argument': { | ||
| // code: ` | ||
| // import boilerplate from '../macro'; | ||
| // var x = doSomething(boilerplate); | ||
| // `, | ||
| // error: true, | ||
| // }, | ||
| // 'invalid usage: missing code string': { | ||
| // code: ` | ||
| // import boilerplate from '../macro'; | ||
| // var x = boilerplate; | ||
| // `, | ||
| // error: true, | ||
| // }, | ||
| }, | ||
| }) |
-119
| const p = require('path') | ||
| const fs = require('fs') | ||
| const requireFromCodeString = require('require-from-string') | ||
| module.exports = { | ||
| requireFromString, | ||
| getReplacement, | ||
| replace, | ||
| resolveModuleContents, | ||
| isCodegenComment, | ||
| isPropertyCall, | ||
| looksLike, | ||
| } | ||
| function requireFromString(code, filename) { | ||
| // Execute the transformed code, as if it were required | ||
| const module = requireFromCodeString(String(code), filename) | ||
| // Allow for es modules (default export) | ||
| return module && module.__esModule ? module.default : module | ||
| } | ||
| function getReplacement({code, fileOpts, args = []}, babel) { | ||
| let module = requireFromString(code, fileOpts.filename) | ||
| // If a function is epxorted, call it with args | ||
| if (typeof module === 'function') { | ||
| module = module(...args) | ||
| } else if (args.length) { | ||
| throw new Error( | ||
| `codegen module (${p.relative( | ||
| process.cwd(), | ||
| fileOpts.filename, | ||
| )}) cannot accept arguments because it does not export a function. You passed the arguments: ${args.join( | ||
| ', ', | ||
| )}`, | ||
| ) | ||
| } | ||
| // Convert whatever we got now (hopefully a string) into AST form | ||
| if (typeof module !== 'string') { | ||
| throw new Error('codegen: Must module.exports a string.') | ||
| } | ||
| return babel.template(module, { | ||
| preserveComments: true, | ||
| placeholderPattern: false, | ||
| ...fileOpts.parserOpts, | ||
| sourceType: 'module', | ||
| })() | ||
| } | ||
| function applyReplacementToPath(replacement, path) { | ||
| if (!replacement) { | ||
| path.remove() | ||
| } else if (Array.isArray(replacement)) { | ||
| path.replaceWithMultiple(replacement) | ||
| } else { | ||
| path.replaceWith(replacement) | ||
| } | ||
| } | ||
| function replace({path, code, fileOpts, args}, babel) { | ||
| const replacement = getReplacement({code, args, fileOpts}, babel) | ||
| applyReplacementToPath(replacement, path) | ||
| } | ||
| function resolveModuleContents({filename, module}) { | ||
| const resolvedPath = p.resolve(p.dirname(filename), module) | ||
| const code = fs.readFileSync(require.resolve(resolvedPath)) | ||
| return {code, resolvedPath} | ||
| } | ||
| function isCodegenComment(comment) { | ||
| const normalisedComment = comment.value | ||
| .trim() | ||
| .split(' ')[0] | ||
| .trim() | ||
| return ( | ||
| normalisedComment.startsWith('codegen') || | ||
| normalisedComment.startsWith('@codegen') | ||
| ) | ||
| } | ||
| function isPropertyCall(path, name) { | ||
| return looksLike(path, { | ||
| node: { | ||
| type: 'CallExpression', | ||
| callee: { | ||
| property: {name}, | ||
| }, | ||
| }, | ||
| }) | ||
| } | ||
| function looksLike(a, b) { | ||
| return ( | ||
| a && | ||
| b && | ||
| Object.keys(b).every(bKey => { | ||
| const bVal = b[bKey] | ||
| const aVal = a[bKey] | ||
| if (typeof bVal === 'function') { | ||
| return bVal(aVal) | ||
| } | ||
| return isPrimitive(bVal) ? bVal === aVal : looksLike(aVal, bVal) | ||
| }) | ||
| ) | ||
| } | ||
| function isPrimitive(val) { | ||
| // eslint-disable-next-line | ||
| return val == null || /^[sbn]/.test(typeof val) | ||
| } | ||
| /* | ||
| eslint | ||
| complexity: ["error", 8], | ||
| import/no-unassigned-import: "off", | ||
| import/no-dynamic-require: "off", | ||
| */ |
| const {createMacro} = require('babel-plugin-macros') | ||
| // const {getReplacement} = require('./helpers') | ||
| module.exports = createMacro(yourNewMacro) | ||
| // function yourNewMacro({references, state, babel}) { | ||
| function yourNewMacro() { | ||
| // do something here | ||
| } |
-176
| const { | ||
| getReplacement, | ||
| replace, | ||
| resolveModuleContents, | ||
| isCodegenComment, | ||
| isPropertyCall, | ||
| requireFromString, | ||
| } = require('./helpers') | ||
| module.exports = getReplacers | ||
| function getReplacers(babel) { | ||
| function asProgram(path, fileOpts) { | ||
| const {code} = babel.transformFromAst(path.node, { | ||
| filename: fileOpts.filename, | ||
| plugins: fileOpts.plugins, | ||
| presets: fileOpts.presets, | ||
| }) | ||
| const replacement = getReplacement({code, fileOpts}, babel) | ||
| path.node.body = Array.isArray(replacement) ? replacement : [replacement] | ||
| } | ||
| function asImportDeclaration(path, fileOpts) { | ||
| const codegenComment = path.node.source.leadingComments | ||
| .find(isCodegenComment) | ||
| .value.trim() | ||
| const {code, resolvedPath} = resolveModuleContents({ | ||
| filename: fileOpts.filename, | ||
| module: path.node.source.value, | ||
| }) | ||
| let args | ||
| if (codegenComment !== 'codegen') { | ||
| args = requireFromString( | ||
| `module.exports = [${codegenComment | ||
| .replace(/codegen\((.*)\)/, '$1') | ||
| .trim()}]`, | ||
| fileOpts.filename, | ||
| ) | ||
| } | ||
| replace( | ||
| { | ||
| path, | ||
| code, | ||
| fileOpts: { | ||
| ...fileOpts, | ||
| filename: resolvedPath, | ||
| }, | ||
| args, | ||
| }, | ||
| babel, | ||
| ) | ||
| } | ||
| function asIdentifier(path, fileOpts) { | ||
| const targetPath = path.parentPath | ||
| switch (targetPath.type) { | ||
| case 'TaggedTemplateExpression': { | ||
| return asTag(targetPath, fileOpts) | ||
| } | ||
| case 'CallExpression': { | ||
| const isCallee = targetPath.get('callee') === path | ||
| if (isCallee) { | ||
| return asFunction(targetPath, fileOpts) | ||
| } else { | ||
| return false | ||
| } | ||
| } | ||
| case 'JSXOpeningElement': { | ||
| const jsxElement = targetPath.parentPath | ||
| return asJSX(jsxElement, fileOpts) | ||
| } | ||
| case 'JSXClosingElement': { | ||
| // ignore the closing element | ||
| // but don't mark as unhandled (return false) | ||
| // we already handled the opening element | ||
| return true | ||
| } | ||
| case 'MemberExpression': { | ||
| const callPath = targetPath.parentPath | ||
| const isRequireCall = isPropertyCall(callPath, 'require') | ||
| if (isRequireCall) { | ||
| return asImportCall(callPath, fileOpts) | ||
| } else { | ||
| return false | ||
| } | ||
| } | ||
| default: { | ||
| return false | ||
| } | ||
| } | ||
| } | ||
| function asImportCall(path, fileOpts) { | ||
| const [source, ...args] = path.get('arguments') | ||
| const {code, resolvedPath} = resolveModuleContents({ | ||
| filename: fileOpts.filename, | ||
| module: source.node.value, | ||
| }) | ||
| const argValues = args.map(a => { | ||
| const result = a.evaluate() | ||
| if (!result.confident) { | ||
| throw new Error( | ||
| 'codegen cannot determine the value of an argument in codegen.require', | ||
| ) | ||
| } | ||
| return result.value | ||
| }) | ||
| replace( | ||
| { | ||
| path, | ||
| code, | ||
| fileOpts: { | ||
| ...fileOpts, | ||
| filename: resolvedPath, | ||
| }, | ||
| args: argValues, | ||
| }, | ||
| babel, | ||
| ) | ||
| } | ||
| function asTag(path, fileOpts) { | ||
| const code = path.get('quasi').evaluate().value | ||
| if (!code) { | ||
| throw path.buildCodeFrameError( | ||
| 'Unable to determine the value of your codegen string', | ||
| Error, | ||
| ) | ||
| } | ||
| replace({path, code, fileOpts}, babel) | ||
| } | ||
| function asFunction(path, fileOpts) { | ||
| const argumentsPaths = path.get('arguments') | ||
| const code = argumentsPaths[0].evaluate().value | ||
| replace( | ||
| { | ||
| path: argumentsPaths[0].parentPath, | ||
| code, | ||
| fileOpts, | ||
| }, | ||
| babel, | ||
| ) | ||
| } | ||
| function asJSX(path, fileOpts) { | ||
| const children = path.get('children') | ||
| let code = children[0].node.expression.value | ||
| if (children[0].node.expression.type === 'TemplateLiteral') { | ||
| code = children[0].get('expression').evaluate().value | ||
| } | ||
| replace( | ||
| { | ||
| path: children[0].parentPath, | ||
| code, | ||
| fileOpts, | ||
| }, | ||
| babel, | ||
| ) | ||
| } | ||
| return { | ||
| asTag, | ||
| asJSX, | ||
| asFunction, | ||
| asProgram, | ||
| asImportCall, | ||
| asImportDeclaration, | ||
| asIdentifier, | ||
| } | ||
| } | ||
| /* | ||
| eslint | ||
| complexity: ["error", 8] | ||
| */ |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
1
-50%0
-100%26733
-36.95%18
-25%235
-58.19%40
-81.82%1
Infinity%- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed
- Removed