do not rewrite assignments
This commit is contained in:
@@ -78,6 +78,34 @@ describe('transform', () => {
|
|||||||
expect(out).not.toContain('import *')
|
expect(out).not.toContain('import *')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('bails when the chain is a write target', () => {
|
||||||
|
const writes = [
|
||||||
|
'app.bsky.feed.like = 1',
|
||||||
|
'app.bsky.feed.like++',
|
||||||
|
'delete app.bsky.feed.like',
|
||||||
|
'for (app.bsky.feed.like of []) {}',
|
||||||
|
';[app.bsky.feed.like] = []',
|
||||||
|
';({x: app.bsky.feed.like} = {})',
|
||||||
|
]
|
||||||
|
for (const stmt of writes) {
|
||||||
|
const out = applyPlugin(
|
||||||
|
`import {app} from './lexicons'\n${stmt}\n`,
|
||||||
|
PROBE_FILE,
|
||||||
|
)
|
||||||
|
expect(out).toContain(`from './lexicons'`)
|
||||||
|
expect(out).not.toContain('import *')
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('rewrites a read of a leaf even when a sibling member is written', () => {
|
||||||
|
const out = applyPlugin(
|
||||||
|
`import {app} from './lexicons'\ndelete app.bsky.feed.like.$cached\n`,
|
||||||
|
PROBE_FILE,
|
||||||
|
)
|
||||||
|
expect(out).toContain('delete _lex_app_bsky_feed_like.$cached')
|
||||||
|
expect(out).not.toContain(`from './lexicons'`)
|
||||||
|
})
|
||||||
|
|
||||||
test('leaves type-only imports untouched', () => {
|
test('leaves type-only imports untouched', () => {
|
||||||
const src = `import type {app} from './lexicons'\nexport type T = typeof app\n`
|
const src = `import type {app} from './lexicons'\nexport type T = typeof app\n`
|
||||||
const out = applyPlugin(src, PROBE_FILE)
|
const out = applyPlugin(src, PROBE_FILE)
|
||||||
|
|||||||
@@ -21,8 +21,8 @@
|
|||||||
* which imports its own copy of the barrel via '../lexicons/index.js'.
|
* which imports its own copy of the barrel via '../lexicons/index.js'.
|
||||||
*
|
*
|
||||||
* Correctness fallback: if any reference to a barrel binding cannot be rewritten
|
* Correctness fallback: if any reference to a barrel binding cannot be rewritten
|
||||||
* (namespace used as a value, computed access, chain ending at a non-leaf), that
|
* (namespace used as a value, computed access, chain ending at a non-leaf,
|
||||||
* binding is left on the barrel import. The result is always correct, merely
|
* chain in a write position), that binding is left on the barrel import. The result is always correct, merely
|
||||||
* unshaken for that file. Set BSKY_LEXICON_IMPORTS_DEBUG=1 to log such bails.
|
* unshaken for that file. Set BSKY_LEXICON_IMPORTS_DEBUG=1 to log such bails.
|
||||||
*
|
*
|
||||||
* Caveat: the rewrite bakes leaf file paths into each consumer's transform
|
* Caveat: the rewrite bakes leaf file paths into each consumer's transform
|
||||||
@@ -96,6 +96,28 @@ function leafFileFor(dir, segment) {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True when the member chain is written to rather than read. Such a chain
|
||||||
|
* cannot be collapsed into a bare identifier: imports are read-only bindings
|
||||||
|
* (assignment/++ would throw where the original property write may not) and
|
||||||
|
* `delete <identifier>` is a strict-mode SyntaxError in the emitted code.
|
||||||
|
*/
|
||||||
|
function isWriteTarget(memberPath) {
|
||||||
|
const parent = memberPath.parentPath
|
||||||
|
if (!parent) return false
|
||||||
|
if (parent.isAssignmentExpression()) {
|
||||||
|
return parent.node.left === memberPath.node
|
||||||
|
}
|
||||||
|
if (parent.isUpdateExpression()) return true
|
||||||
|
if (parent.isUnaryExpression({operator: 'delete'})) return true
|
||||||
|
if (parent.isForXStatement()) return parent.node.left === memberPath.node
|
||||||
|
if (parent.isArrayPattern() || parent.isRestElement()) return true
|
||||||
|
if (parent.isObjectProperty() && parent.parentPath.isObjectPattern()) {
|
||||||
|
return parent.node.value === memberPath.node
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
/** `rootDir\0segments\0leafFile` -> boolean */
|
/** `rootDir\0segments\0leafFile` -> boolean */
|
||||||
const chainCache = new Map()
|
const chainCache = new Map()
|
||||||
|
|
||||||
@@ -208,6 +230,7 @@ module.exports = function lexiconLeafImports(babel, options = {}) {
|
|||||||
for (;;) {
|
for (;;) {
|
||||||
const kind = classify(dir, segment)
|
const kind = classify(dir, segment)
|
||||||
if (kind === 'file') {
|
if (kind === 'file') {
|
||||||
|
if (isWriteTarget(cur)) return null
|
||||||
return {memberPath: cur, leafFile: leafFileFor(dir, segment), segments}
|
return {memberPath: cur, leafFile: leafFileFor(dir, segment), segments}
|
||||||
}
|
}
|
||||||
if (kind !== 'dir') return null
|
if (kind !== 'dir') return null
|
||||||
|
|||||||
Reference in New Issue
Block a user