-
Notifications
You must be signed in to change notification settings - Fork 108
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add integrity checking to tests and fix various bugs
Fixes #115 This commit adds a new `IntegrityCheckingMagicString` class that subclasses `MagicString` and does an integrity check operation, which is used in test code. This revealed 5 bugs, all of which are also fixed in this commit: * Within `indent`, the code was calling `chunk.split`, then making an attempt at properly updating `byStart` and `byEnd`. But it wasn't properly updating `lastChunk`. Rather than calling `chunk.split`, we can just call `this._splitChunk`, which updates `byStart`, `byEnd`, and `lastChunk` correctly. * `move` could sometimes assign `undefined` as a `next` pointer, which is inconsistent since all other cases use `null`. Not a really big deal, but nice to be consistent. * `overwrite` was attempting to do a linked list removal in a way that wasn't properly updating all state ( #115 ). I tried changing the code to remove the nodes properly, but the details seemed to get really subtle, especially accounting for moved chunks, and it didn't seem like the complexity was worth it, so I effectively just reverted #113 to fix the issue. But now with the new integrity checking, it should be a lot easier to get the details right if that's desirable. * `trimStart` and `trimEnd` were attempting to update `byStart` and `byEnd`, but they didn't properly set `byEnd` of the newly-created chunk created by `chunk.trimStart`/`chunk.trimEnd`. Setting the `byEnd` for that new chunk seems to fix things. * `trimEnd` was setting `this.lastChunk = chunk.next`, which makes sense for the first iteration (when working with the very last chunk in the linked list), but doesn't make sense for later iterations, since it meant that some chunks just get dropped. We now only run that line on the last chunk.
- Loading branch information
1 parent
46ea184
commit 8a85c8c
Showing
4 changed files
with
74 additions
and
18 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
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
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,46 @@ | ||
const MagicString = require( '../../' ); | ||
const assert = require( 'assert' ); | ||
|
||
class IntegrityCheckingMagicString extends MagicString { | ||
checkIntegrity () { | ||
let prevChunk = null; | ||
let chunk = this.firstChunk; | ||
let numNodes = 0; | ||
while ( chunk ) { | ||
assert.strictEqual( this.byStart[chunk.start], chunk ); | ||
assert.strictEqual( this.byEnd[chunk.end], chunk ); | ||
assert.strictEqual( chunk.previous, prevChunk ); | ||
if ( prevChunk ) { | ||
assert.strictEqual( prevChunk.next, chunk ); | ||
} | ||
prevChunk = chunk; | ||
chunk = chunk.next; | ||
numNodes++; | ||
} | ||
assert.strictEqual( prevChunk, this.lastChunk ); | ||
assert.strictEqual( this.lastChunk.next, null ); | ||
assert.strictEqual( Object.keys(this.byStart).length, numNodes ); | ||
assert.strictEqual( Object.keys(this.byEnd).length, numNodes ); | ||
} | ||
} | ||
|
||
for (const key in MagicString.prototype) { | ||
if ( !MagicString.prototype.hasOwnProperty( key ) ) { | ||
continue; | ||
} | ||
const func = MagicString.prototype[key]; | ||
if ( typeof func === 'function' ) { | ||
IntegrityCheckingMagicString.prototype[key] = function () { | ||
const result = func.apply( this, arguments ); | ||
try { | ||
this.checkIntegrity(); | ||
} catch ( e ) { | ||
e.message = `Integrity error after invoking ${key}:\n${e.message}`; | ||
throw e; | ||
} | ||
return result; | ||
}; | ||
} | ||
} | ||
|
||
module.exports = IntegrityCheckingMagicString; |