feature: vor und nachteile

This commit is contained in:
Hikyu
2026-05-31 13:54:31 +02:00
parent 552a947a1c
commit e3dad17446
23270 changed files with 3024794 additions and 200 deletions
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022 Anthony Fu <https://github.com/antfu>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+74
View File
@@ -0,0 +1,74 @@
# magic-string-stack
[![npm version][npm-version-src]][npm-version-href]
[![npm downloads][npm-downloads-src]][npm-downloads-href]
[![bundle][bundle-src]][bundle-href]
[![JSDocs][jsdocs-src]][jsdocs-href]
[![License][license-src]][license-href]
[`magic-string`](https://github.com/rich-harris/magic-string) with the capability of committing changes.
One of the great features of MagicString is that it always relates to the original string positions. However, in some cases, you want to make changes on top of the previously changed string. Usually, you will need to create a new `MagicString` instance and apply the changes again, which then will end up with multiple sourcemaps that you also need to combine manually. This package makes it magically work on a single instance and generate a single auto-combined sourcemap.
This package extends `MagicString` class by adding two methods `.commit()` and `.rollback()`. Under the hood, it also proxies all the operations methods.
## Usage
### `.commit()`
Commit all the changes made so far to MagicString. `s.original` will become the current transformed result. And the positions will now be based on the new string. Under the hood, it creates a new `MagicString` instance and swaps all the methods to the new instance.
### `.rollback()`
Rollback to the state before the last commit. It throws an error if there is no previous commit.
### `.generateMap()`
Supercharge the original `generateMap` method. Where there are multiple commits, it will generate a combined sourcemap using [`@ampproject/remapping`](https://github.com/ampproject/remapping).
### Example
```ts
import MagicStringStack from 'magic-string-stack'
const s = new MagicStringStack('problems = 99')
s.replace('problems', 'issues')
.prepend('var ')
s.toString() // 'var issues = 99'
s.original // 'problems = 99' (original string)
s.commit() // this will commit the changes
s.original // 'var issues = 99' (applied with previous changes)
s.replace('issues', 'problems')
s.toString() // 'var problems = 99'
s.generateMap() // generate sourcemap, if multiple commits happened, it will generate a combined sourcemap
```
## Sponsors
<p align="center">
<a href="https://cdn.jsdelivr.net/gh/antfu/static/sponsors.svg">
<img src='https://cdn.jsdelivr.net/gh/antfu/static/sponsors.svg'/>
</a>
</p>
## License
[MIT](./LICENSE) License © 2023-PRESENT [Anthony Fu](https://github.com/antfu)
<!-- Badges -->
[npm-version-src]: https://img.shields.io/npm/v/magic-string-stack?style=flat&colorA=080f12&colorB=1fa669
[npm-version-href]: https://npmjs.com/package/magic-string-stack
[npm-downloads-src]: https://img.shields.io/npm/dm/magic-string-stack?style=flat&colorA=080f12&colorB=1fa669
[npm-downloads-href]: https://npmjs.com/package/magic-string-stack
[bundle-src]: https://img.shields.io/bundlephobia/minzip/magic-string-stack?style=flat&colorA=080f12&colorB=1fa669&label=minzip
[bundle-href]: https://bundlephobia.com/result?p=magic-string-stack
[license-src]: https://img.shields.io/github/license/antfu/magic-string-stack.svg?style=flat&colorA=080f12&colorB=1fa669
[license-href]: https://github.com/antfu/magic-string-stack/blob/main/LICENSE
[jsdocs-src]: https://img.shields.io/badge/jsdocs-reference-080f12?style=flat&colorA=080f12&colorB=1fa669
[jsdocs-href]: https://www.jsdocs.io/package/magic-string-stack
+107
View File
@@ -0,0 +1,107 @@
'use strict';
const remapping = require('@ampproject/remapping');
const MagicString = require('magic-string');
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
const remapping__default = /*#__PURE__*/_interopDefaultCompat(remapping);
const MagicString__default = /*#__PURE__*/_interopDefaultCompat(MagicString);
class MagicStringStack {
constructor(content, _options) {
this._options = _options;
this._current = new MagicString__default(content, this._options);
this._stack.unshift(this._current);
const proxy = new Proxy(new MagicString__default(""), {
get: (_, p, receiver) => {
if (Reflect.has(this, p))
return Reflect.get(this, p, receiver);
const parent = Reflect.get(this._current, p, receiver);
if (typeof parent === "function") {
return (...args) => {
const result = parent.apply(this._current, args);
if (result === this._current)
return proxy;
return result;
};
}
return parent;
},
set: (_, p, value) => {
if (Reflect.has(this, p))
return Reflect.set(this, p, value, this);
return Reflect.set(this._current, p, value);
}
});
return proxy;
}
/**
* The stack of MagicString instances.
* Lastest instance is pushed to the front of the array.
*/
_stack = [];
/**
* Prepresents the current MagicString instance.
* It should be in the this._stack[0]
*/
_current;
/**
* Commit current changes and reset the `.original` property and the indices.
*
* When running `generateMap`, the sourcemaps will be generated and merged into a single sourcemap.
*/
commit() {
const newOne = new MagicString__default(this._current.toString(), this._options);
newOne.offset = this._current.offset;
this._current = newOne;
this._stack.unshift(newOne);
return this;
}
/**
* Rollback to the previous commit.
*/
rollback() {
if (this._stack.length <= 1)
throw new Error("Cannot rollback on the first commit");
this._stack.shift();
this._current = this._stack[0];
return this;
}
get original() {
return this._current.original;
}
toString() {
return this._current.toString();
}
clone() {
const s = new MagicStringStack(this._current.toString(), this._options);
s._stack = this._stack.map((s2) => s2.clone());
s._current = s._stack[0];
return s;
}
/**
* Generates a version 3 sourcemap.
*/
generateMap(options) {
return new MagicString.SourceMap(this.generateDecodedMap(options));
}
/**
* Generates a sourcemap object with raw mappings in array form, rather than encoded as a string.
* Useful if you need to manipulate the sourcemap further, but most of the time you will use `generateMap` instead.
*/
generateDecodedMap(options) {
if (this._stack.length === 1)
return this._current.generateDecodedMap(options);
const SOURCE_PLACEHOLDER = "__magic_string_placeholder__.js";
const maps = this._stack.map((s) => s.generateDecodedMap({ source: SOURCE_PLACEHOLDER, ...options }));
const merged = remapping__default(maps, () => null, { decodedMappings: true });
merged.sources = merged.sources.map((source) => source === SOURCE_PLACEHOLDER ? "" : source);
delete merged.version;
return {
...merged
};
}
}
module.exports = MagicStringStack;
+44
View File
@@ -0,0 +1,44 @@
import MagicString, { MagicStringOptions, SourceMapOptions, SourceMap, DecodedSourceMap } from 'magic-string';
interface MagicStringStackType extends MagicString {
}
interface MagicStringStack extends MagicStringStackType {
}
declare class MagicStringStack implements MagicStringStackType {
private _options?;
/**
* The stack of MagicString instances.
* Lastest instance is pushed to the front of the array.
*/
private _stack;
/**
* Prepresents the current MagicString instance.
* It should be in the this._stack[0]
*/
private _current;
constructor(content: string, _options?: MagicStringOptions | undefined);
/**
* Commit current changes and reset the `.original` property and the indices.
*
* When running `generateMap`, the sourcemaps will be generated and merged into a single sourcemap.
*/
commit(): this;
/**
* Rollback to the previous commit.
*/
rollback(): this;
get original(): string;
toString(): string;
clone(): this;
/**
* Generates a version 3 sourcemap.
*/
generateMap(options?: SourceMapOptions): SourceMap;
/**
* Generates a sourcemap object with raw mappings in array form, rather than encoded as a string.
* Useful if you need to manipulate the sourcemap further, but most of the time you will use `generateMap` instead.
*/
generateDecodedMap(options?: SourceMapOptions): DecodedSourceMap;
}
export = MagicStringStack;
+44
View File
@@ -0,0 +1,44 @@
import MagicString, { MagicStringOptions, SourceMapOptions, SourceMap, DecodedSourceMap } from 'magic-string';
interface MagicStringStackType extends MagicString {
}
interface MagicStringStack extends MagicStringStackType {
}
declare class MagicStringStack implements MagicStringStackType {
private _options?;
/**
* The stack of MagicString instances.
* Lastest instance is pushed to the front of the array.
*/
private _stack;
/**
* Prepresents the current MagicString instance.
* It should be in the this._stack[0]
*/
private _current;
constructor(content: string, _options?: MagicStringOptions | undefined);
/**
* Commit current changes and reset the `.original` property and the indices.
*
* When running `generateMap`, the sourcemaps will be generated and merged into a single sourcemap.
*/
commit(): this;
/**
* Rollback to the previous commit.
*/
rollback(): this;
get original(): string;
toString(): string;
clone(): this;
/**
* Generates a version 3 sourcemap.
*/
generateMap(options?: SourceMapOptions): SourceMap;
/**
* Generates a sourcemap object with raw mappings in array form, rather than encoded as a string.
* Useful if you need to manipulate the sourcemap further, but most of the time you will use `generateMap` instead.
*/
generateDecodedMap(options?: SourceMapOptions): DecodedSourceMap;
}
export { MagicStringStack as default };
+44
View File
@@ -0,0 +1,44 @@
import MagicString, { MagicStringOptions, SourceMapOptions, SourceMap, DecodedSourceMap } from 'magic-string';
interface MagicStringStackType extends MagicString {
}
interface MagicStringStack extends MagicStringStackType {
}
declare class MagicStringStack implements MagicStringStackType {
private _options?;
/**
* The stack of MagicString instances.
* Lastest instance is pushed to the front of the array.
*/
private _stack;
/**
* Prepresents the current MagicString instance.
* It should be in the this._stack[0]
*/
private _current;
constructor(content: string, _options?: MagicStringOptions | undefined);
/**
* Commit current changes and reset the `.original` property and the indices.
*
* When running `generateMap`, the sourcemaps will be generated and merged into a single sourcemap.
*/
commit(): this;
/**
* Rollback to the previous commit.
*/
rollback(): this;
get original(): string;
toString(): string;
clone(): this;
/**
* Generates a version 3 sourcemap.
*/
generateMap(options?: SourceMapOptions): SourceMap;
/**
* Generates a sourcemap object with raw mappings in array form, rather than encoded as a string.
* Useful if you need to manipulate the sourcemap further, but most of the time you will use `generateMap` instead.
*/
generateDecodedMap(options?: SourceMapOptions): DecodedSourceMap;
}
export { MagicStringStack as default };
+100
View File
@@ -0,0 +1,100 @@
import remapping from '@ampproject/remapping';
import MagicString, { SourceMap } from 'magic-string';
class MagicStringStack {
constructor(content, _options) {
this._options = _options;
this._current = new MagicString(content, this._options);
this._stack.unshift(this._current);
const proxy = new Proxy(new MagicString(""), {
get: (_, p, receiver) => {
if (Reflect.has(this, p))
return Reflect.get(this, p, receiver);
const parent = Reflect.get(this._current, p, receiver);
if (typeof parent === "function") {
return (...args) => {
const result = parent.apply(this._current, args);
if (result === this._current)
return proxy;
return result;
};
}
return parent;
},
set: (_, p, value) => {
if (Reflect.has(this, p))
return Reflect.set(this, p, value, this);
return Reflect.set(this._current, p, value);
}
});
return proxy;
}
/**
* The stack of MagicString instances.
* Lastest instance is pushed to the front of the array.
*/
_stack = [];
/**
* Prepresents the current MagicString instance.
* It should be in the this._stack[0]
*/
_current;
/**
* Commit current changes and reset the `.original` property and the indices.
*
* When running `generateMap`, the sourcemaps will be generated and merged into a single sourcemap.
*/
commit() {
const newOne = new MagicString(this._current.toString(), this._options);
newOne.offset = this._current.offset;
this._current = newOne;
this._stack.unshift(newOne);
return this;
}
/**
* Rollback to the previous commit.
*/
rollback() {
if (this._stack.length <= 1)
throw new Error("Cannot rollback on the first commit");
this._stack.shift();
this._current = this._stack[0];
return this;
}
get original() {
return this._current.original;
}
toString() {
return this._current.toString();
}
clone() {
const s = new MagicStringStack(this._current.toString(), this._options);
s._stack = this._stack.map((s2) => s2.clone());
s._current = s._stack[0];
return s;
}
/**
* Generates a version 3 sourcemap.
*/
generateMap(options) {
return new SourceMap(this.generateDecodedMap(options));
}
/**
* Generates a sourcemap object with raw mappings in array form, rather than encoded as a string.
* Useful if you need to manipulate the sourcemap further, but most of the time you will use `generateMap` instead.
*/
generateDecodedMap(options) {
if (this._stack.length === 1)
return this._current.generateDecodedMap(options);
const SOURCE_PLACEHOLDER = "__magic_string_placeholder__.js";
const maps = this._stack.map((s) => s.generateDecodedMap({ source: SOURCE_PLACEHOLDER, ...options }));
const merged = remapping(maps, () => null, { decodedMappings: true });
merged.sources = merged.sources.map((source) => source === SOURCE_PLACEHOLDER ? "" : source);
delete merged.version;
return {
...merged
};
}
}
export { MagicStringStack as default };
+80
View File
@@ -0,0 +1,80 @@
{
"name": "magic-string-stack",
"type": "module",
"version": "0.1.2",
"packageManager": "pnpm@9.15.4",
"description": "magic-string with the capability of committing changes",
"author": "Anthony Fu <anthonyfu117@hotmail.com>",
"license": "MIT",
"funding": "https://github.com/sponsors/antfu",
"homepage": "https://github.com/antfu/magic-string-stack#readme",
"repository": {
"type": "git",
"url": "git+https://github.com/antfu/magic-string-stack.git"
},
"bugs": "https://github.com/antfu/magic-string-stack/issues",
"keywords": [
"magic-string",
"sourcemap"
],
"sideEffects": false,
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.mjs",
"require": "./dist/index.cjs"
}
},
"main": "./dist/index.mjs",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
"typesVersions": {
"*": {
"*": [
"./dist/*",
"./dist/index.d.ts"
]
}
},
"files": [
"dist"
],
"scripts": {
"build": "unbuild",
"dev": "unbuild --stub",
"lint": "eslint .",
"prepublishOnly": "nr build",
"release": "bumpp && npm publish",
"start": "esno src/index.ts",
"test": "vitest",
"typecheck": "tsc --noEmit",
"prepare": "simple-git-hooks"
},
"dependencies": {
"@ampproject/remapping": "^2.3.0",
"magic-string": "^0.30.17"
},
"devDependencies": {
"@antfu/eslint-config": "^4.1.0",
"@antfu/ni": "^23.3.0",
"@antfu/utils": "^8.1.0",
"@types/node": "^22.12.0",
"bumpp": "^10.0.1",
"eslint": "^9.19.0",
"esno": "^4.8.0",
"lint-staged": "^15.4.3",
"pnpm": "^9.15.4",
"rimraf": "^6.0.1",
"simple-git-hooks": "^2.11.1",
"typescript": "^5.7.3",
"unbuild": "^3.3.1",
"vite": "^6.0.11",
"vitest": "^3.0.4"
},
"simple-git-hooks": {
"pre-commit": "pnpm lint-staged"
},
"lint-staged": {
"*": "eslint --fix"
}
}