-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #68 from andrico1234/feature/define-properties
add `define-properties` codemod
- Loading branch information
Showing
14 changed files
with
691 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
import jscodeshift from 'jscodeshift'; | ||
import { | ||
DEFAULT_IMPORT, | ||
getImportIdentifierMap, | ||
getVariableExpressionHasIdentifier, | ||
insertAfterImports, | ||
insertCommentAboveNode, | ||
removeImport, | ||
replaceDefaultImport, | ||
replaceRequireMemberExpression, | ||
} from '../shared.js'; | ||
import { dir } from 'console'; | ||
|
||
/** | ||
* @typedef {import('../../types.js').Codemod} Codemod | ||
* @typedef {import('../../types.js').CodemodOptions} CodemodOptions | ||
*/ | ||
|
||
/** | ||
* | ||
* @param {string} name | ||
* @returns | ||
*/ | ||
const definePropertiesTemplate = (name) => ` | ||
const ${name} = function (object, map) { | ||
let propKeys = Object.keys(map); | ||
propKeys = propKeys.concat(Object.getOwnPropertySymbols(map)); | ||
for (var i = 0; i < propKeys.length; i += 1) { | ||
const propKey = propKeys[i]; | ||
const value = map[propKey]; | ||
if (propKey in object) { | ||
continue; | ||
} | ||
Object.defineProperty(object, propKey, { | ||
value, | ||
configurable: true, | ||
enumerable: false, | ||
writable: true, | ||
}) | ||
} | ||
return object; | ||
};`; | ||
|
||
/** | ||
* @param {CodemodOptions} [options] | ||
* @returns {Codemod} | ||
*/ | ||
export default function (options) { | ||
return { | ||
name: 'define-properties', | ||
transform: ({ file }) => { | ||
const j = jscodeshift; | ||
const root = j(file.source); | ||
const variableExpressionHasIdentifier = | ||
getVariableExpressionHasIdentifier( | ||
'define-properties', | ||
'supportsDescriptors', | ||
root, | ||
j, | ||
); | ||
|
||
// Use case 1: require('define-properties').supportsDescriptors | ||
if (variableExpressionHasIdentifier) { | ||
const didReplace = replaceRequireMemberExpression( | ||
'define-properties', | ||
true, | ||
root, | ||
j, | ||
); | ||
return didReplace ? root.toSource(options) : file.source; | ||
} | ||
|
||
const map = getImportIdentifierMap('define-properties', root, j); | ||
|
||
const identifier = map[DEFAULT_IMPORT]; | ||
|
||
const callExpressions = root.find(j.CallExpression, { | ||
callee: { | ||
name: identifier, | ||
}, | ||
}); | ||
|
||
if (!callExpressions.length) { | ||
removeImport('define-properties', root, j); | ||
return root.toSource(options); | ||
} | ||
|
||
let transformCount = 0; | ||
let dirty = false; | ||
|
||
callExpressions.forEach((path) => { | ||
const node = path.node; | ||
const newIdentifier = `$${identifier}`; | ||
|
||
// Use case 2: define(object, map); | ||
if (node.arguments.length === 2) { | ||
if (transformCount === 0) { | ||
const defineFunction = definePropertiesTemplate(newIdentifier); | ||
insertAfterImports(defineFunction, root, j); | ||
} | ||
|
||
// Not all call expressions have a name property, but node.callee should be of type Identifi | ||
if ('name' in node.callee) { | ||
node.callee.name = newIdentifier; | ||
} | ||
|
||
transformCount++; | ||
dirty = true; | ||
} | ||
|
||
// Use case 3: define(object, map, predicates); | ||
if (node.arguments.length === 3) { | ||
const comment = j.commentBlock( | ||
'\n This usage of `define-properties` usage can be cleaned up through a mix of Object.defineProperty() and a custom predicate function.\n details can be found here: https://github.com/es-tooling/module-replacements-codemods/issues/66 \n', | ||
true, | ||
false, | ||
); | ||
|
||
const startLine = node.loc?.start.line ?? 0; | ||
|
||
insertCommentAboveNode(comment, startLine, root, j); | ||
|
||
dirty = true; | ||
} | ||
}); | ||
|
||
if (transformCount === callExpressions.length) { | ||
removeImport('define-properties', root, j); | ||
} | ||
|
||
return dirty ? root.toSource(options) : file.source; | ||
}, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
const sup = true; | ||
|
||
if (sup) { | ||
console.log('supports descriptors'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
const sup = require('define-properties').supportsDescriptors; | ||
|
||
if (sup) { | ||
console.log('supports descriptors'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
const sup = true; | ||
|
||
if (sup) { | ||
console.log('supports descriptors'); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
const assert = require('assert'); | ||
|
||
|
||
const $define = function (object, map) { | ||
let propKeys = Object.keys(map); | ||
propKeys = propKeys.concat(Object.getOwnPropertySymbols(map)); | ||
|
||
for (var i = 0; i < propKeys.length; i += 1) { | ||
const propKey = propKeys[i]; | ||
const value = map[propKey]; | ||
|
||
if (propKey in object) { | ||
continue; | ||
} | ||
|
||
Object.defineProperty(object, propKey, { | ||
value, | ||
configurable: true, | ||
enumerable: false, | ||
writable: true, | ||
}) | ||
} | ||
|
||
return object; | ||
}; | ||
|
||
const object1 = { a: 1, b: 2 }; | ||
|
||
const res1 = $define(object1, { | ||
a: 10, | ||
b: 20, | ||
c: 30, | ||
[Symbol.for('d')]: 40 | ||
}); | ||
|
||
assert(res1.a === 1); | ||
assert(res1.b === 2); | ||
assert(res1.c === 30); | ||
assert(res1[Symbol.for('d')] === 40); | ||
|
||
assert.deepEqual(res1, { | ||
a: 1, | ||
b: 2 | ||
}) | ||
|
||
assert.deepEqual(Object.keys(res1), ['a', 'b']); | ||
assert.deepEqual(Object.getOwnPropertyNames(res1), ['a', 'b', 'c']); | ||
assert.deepEqual(Object.getOwnPropertyDescriptor(res1, 'c'), { | ||
configurable: true, | ||
enumerable: false, | ||
value: 30, | ||
writable: true | ||
}); | ||
|
||
const object2 = { a: 1, b: 2 }; | ||
|
||
$define(object2, { | ||
c: 30 | ||
}) | ||
|
||
assert(object2.a === 1); | ||
assert(object2.b === 2); | ||
assert(object2.c === 30); |
Oops, something went wrong.