@dxup/vanilla 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +20 -12
  2. package/dist/index.cjs +200 -25
  3. package/package.json +6 -3
package/README.md CHANGED
@@ -36,15 +36,23 @@ Add the following to your `tsconfig.json`:
36
36
 
37
37
  ### 1. signature
38
38
 
39
- Go to definition for signature parameters.
40
-
41
- ```ts
42
- export default defineConfig({
43
- plugins: [{
44
- name: "testify",
45
- transform(code, id, options) {
46
- // ^^^^^^^
47
- },
48
- }],
49
- });
50
- ```
39
+ - Go to definition for signature parameters.
40
+
41
+ ```ts
42
+ export default defineConfig({
43
+ plugins: [{
44
+ name: "testify",
45
+ transform(code, id, options) {
46
+ // ^^^^^^^
47
+ },
48
+ }],
49
+ });
50
+ ```
51
+
52
+ - Rewrite refactor for signature parameters.
53
+
54
+ ```ts
55
+ function swap(foo: number, bar: string, baz: boolean, qux: symbol) {}
56
+ // ^^^ Refactor > Move parameter right
57
+ swap(2, "3", true, Symbol(5));
58
+ ```
package/dist/index.cjs CHANGED
@@ -12,33 +12,65 @@ function* binaryVisit(ts, sourceFile, node, position) {
12
12
  let right = nodes.length - 1;
13
13
  while (left <= right) {
14
14
  const mid = Math.floor((left + right) / 2);
15
- const node$1 = nodes[mid];
16
- if (position > node$1.getEnd()) left = mid + 1;
17
- else if (position < node$1.getStart(sourceFile)) right = mid - 1;
15
+ const node = nodes[mid];
16
+ if (position > node.getEnd()) left = mid + 1;
17
+ else if (position < node.getStart(sourceFile)) right = mid - 1;
18
18
  else {
19
- yield node$1;
20
- yield* binaryVisit(ts, sourceFile, node$1, position);
19
+ yield node;
20
+ yield* binaryVisit(ts, sourceFile, node, position);
21
21
  return;
22
22
  }
23
23
  }
24
24
  }
25
25
 
26
26
  //#endregion
27
- //#region src/index.ts
28
- const plugin = (module$1) => {
29
- const { typescript: ts } = module$1;
30
- return { create(info) {
31
- for (const [key, method] of [["getDefinitionAndBoundSpan", getDefinitionAndBoundSpan]]) {
32
- const original = info.languageService[key];
33
- info.languageService[key] = method(ts, info, original);
34
- }
35
- return info.languageService;
36
- } };
37
- };
38
- var src_default = plugin;
39
- function getDefinitionAndBoundSpan(ts, info, getDefinitionAndBoundSpan$1) {
27
+ //#region src/shared.ts
28
+ const refactors = { rewrite: { parameter: {
29
+ forward: {
30
+ name: "Move parameter left",
31
+ description: "Move parameter left",
32
+ kind: "refactor.rewrite.parameter.forward"
33
+ },
34
+ backward: {
35
+ name: "Move parameter right",
36
+ description: "Move parameter right",
37
+ kind: "refactor.rewrite.parameter.backward"
38
+ },
39
+ remove: {
40
+ name: "Remove parameter",
41
+ description: "Remove parameter",
42
+ kind: "refactor.rewrite.parameter.remove"
43
+ }
44
+ } } };
45
+
46
+ //#endregion
47
+ //#region src/features/getApplicableRefactors.ts
48
+ function getApplicableRefactors(ts, info, getApplicableRefactors) {
49
+ return (...args) => {
50
+ const result = getApplicableRefactors(...args);
51
+ const sourceFile = info.languageService.getProgram().getSourceFile(args[0]);
52
+ if (!sourceFile) return result;
53
+ let node;
54
+ const position = typeof args[1] === "number" ? args[1] : args[1].pos;
55
+ for (const child of forEachTouchingNode(ts, sourceFile, position)) if (ts.isParameter(child)) node = child;
56
+ if (node?.parent.name) result.push({
57
+ name: "Dxup",
58
+ description: "Dxup refactor actions",
59
+ actions: [
60
+ refactors.rewrite.parameter.forward,
61
+ refactors.rewrite.parameter.backward,
62
+ refactors.rewrite.parameter.remove
63
+ ]
64
+ });
65
+ return result;
66
+ };
67
+ }
68
+
69
+ //#endregion
70
+ //#region src/features/getDefinitionAndBoundSpan.ts
71
+ function getDefinitionAndBoundSpan(ts, info, getDefinitionAndBoundSpan) {
40
72
  return (...args) => {
41
- const result = getDefinitionAndBoundSpan$1(...args);
73
+ const result = getDefinitionAndBoundSpan(...args);
42
74
  if (!result?.definitions?.length) return result;
43
75
  if (result.definitions[0].kind === ts.ScriptElementKind.parameterElement && result.definitions[0].textSpan.start === result.textSpan.start) {
44
76
  const program = info.languageService.getProgram();
@@ -56,15 +88,15 @@ function getDefinitionAndBoundSpan(ts, info, getDefinitionAndBoundSpan$1) {
56
88
  for (const declaration of forEachParameter(checker, signature)) {
57
89
  if (i++ !== index) continue;
58
90
  if (declaration) {
59
- const sourceFile$1 = declaration.getSourceFile();
91
+ const sourceFile = declaration.getSourceFile();
60
92
  definitions.push({
61
- fileName: sourceFile$1.fileName,
93
+ fileName: sourceFile.fileName,
62
94
  textSpan: {
63
- start: declaration.getStart(sourceFile$1),
64
- length: declaration.getWidth(sourceFile$1)
95
+ start: declaration.getStart(sourceFile),
96
+ length: declaration.getWidth(sourceFile)
65
97
  },
66
98
  kind: ts.ScriptElementKind.parameterElement,
67
- name: declaration.getText(sourceFile$1),
99
+ name: declaration.getText(sourceFile),
68
100
  containerKind: ts.ScriptElementKind.unknown,
69
101
  containerName: ""
70
102
  });
@@ -110,4 +142,147 @@ function* forEachParameter(checker, signature) {
110
142
  }
111
143
 
112
144
  //#endregion
113
- module.exports = src_default;
145
+ //#region src/features/getEditsForRefactor.ts
146
+ function getEditsForRefactor(ts, info, getEditsForRefactor) {
147
+ return (...args) => {
148
+ if (args[3] !== "Dxup") return getEditsForRefactor(...args);
149
+ const sourceFile = info.languageService.getProgram().getSourceFile(args[0]);
150
+ const position = typeof args[2] === "number" ? args[2] : args[2].pos;
151
+ const direction = args[4] === refactors.rewrite.parameter.forward.name ? -1 : args[4] === refactors.rewrite.parameter.backward.name ? 1 : args[4] === refactors.rewrite.parameter.remove.name ? 0 : null;
152
+ if (direction === null) return;
153
+ let node;
154
+ for (const child of forEachTouchingNode(ts, sourceFile, position)) if (ts.isParameter(child)) node = child;
155
+ if (!node?.parent.name) return;
156
+ const firstArg = node.parent.parameters[0];
157
+ const withThis = ts.isIdentifier(firstArg.name) && firstArg.name.text === "this";
158
+ const index = node.parent.parameters.indexOf(node) - Number(withThis);
159
+ if (index === -1 || index + direction < 0 || index + direction === node.parent.parameters.length || direction !== 0 && (node.dotDotDotToken || node.parent.parameters[index + direction].dotDotDotToken)) return;
160
+ const modifier = direction === 0 && node.dotDotDotToken ? Infinity : direction;
161
+ const fileTextChanges = {};
162
+ const references = forEachSignatureReference(ts, info.languageService, args[0], node.parent.name.getStart(sourceFile));
163
+ for (const node of references) {
164
+ const sourceFile = node.getSourceFile();
165
+ const textChanges = fileTextChanges[sourceFile.fileName] ??= [];
166
+ if (ts.isCallExpression(node)) textChanges.push(...calculateTextChanges(ts, sourceFile, node.arguments, index, modifier));
167
+ else {
168
+ let index2 = index;
169
+ const firstArg = node.parameters[0];
170
+ if (ts.isIdentifier(firstArg.name) && firstArg.name.text === "this") index2++;
171
+ textChanges.push(...calculateTextChanges(ts, sourceFile, node.parameters, index2, modifier));
172
+ }
173
+ }
174
+ return { edits: Object.entries(fileTextChanges).map(([fileName, textChanges]) => ({
175
+ fileName,
176
+ textChanges
177
+ })) };
178
+ };
179
+ }
180
+ function* forEachSignatureReference(ts, languageService, fileName, position, visited = /* @__PURE__ */ new Set()) {
181
+ const program = languageService.getProgram();
182
+ const references = languageService.getReferencesAtPosition(fileName, position) ?? [];
183
+ outer: for (const { fileName, textSpan } of references) {
184
+ const key = fileName + "@" + textSpan.start;
185
+ if (visited.has(key)) continue;
186
+ visited.add(key);
187
+ const sourceFile = program.getSourceFile(fileName);
188
+ let node;
189
+ for (const child of forEachTouchingNode(ts, sourceFile, textSpan.start)) if (ts.isIdentifier(child)) {
190
+ node = child;
191
+ break;
192
+ }
193
+ if (!node) continue;
194
+ if (ts.isCallExpression(node.parent) && node === node.parent.expression) {
195
+ yield node.parent;
196
+ continue;
197
+ }
198
+ if (ts.isPropertyAccessExpression(node.parent) && node === node.parent.name && ts.isCallExpression(node.parent.parent) && node.parent === node.parent.parent.expression) {
199
+ yield node.parent.parent;
200
+ continue;
201
+ }
202
+ if (ts.isFunctionLike(node.parent) && node === node.parent.name) {
203
+ yield node.parent;
204
+ continue;
205
+ }
206
+ if ((ts.isPropertyAssignment(node.parent) || ts.isPropertyDeclaration(node.parent)) && node === node.parent.name && node.parent.initializer) {
207
+ const expression = getUnwrappedExpression(ts, node.parent.initializer);
208
+ if (ts.isFunctionLike(expression)) {
209
+ yield expression;
210
+ continue;
211
+ }
212
+ }
213
+ let start;
214
+ let curr = node;
215
+ inner: while (curr) {
216
+ if (ts.isVariableDeclaration(curr.parent) && curr === curr.parent.initializer) start = curr.parent.name.getStart(sourceFile);
217
+ else if ((ts.isPropertyAssignment(curr.parent) || ts.isPropertyDeclaration(curr.parent)) && curr === curr.parent.initializer) start = curr.parent.name.getStart(sourceFile);
218
+ else if (ts.isShorthandPropertyAssignment(curr.parent)) start = curr.getStart(sourceFile);
219
+ else if (ts.isTypeQueryNode(curr.parent) && curr === curr.parent.exprName && ts.isVariableDeclaration(curr.parent.parent) && curr.parent.parent.initializer) {
220
+ const expression = getUnwrappedExpression(ts, curr.parent.parent.initializer);
221
+ if (ts.isFunctionLike(expression)) yield expression;
222
+ start = curr.parent.parent.name.getStart(sourceFile);
223
+ } else if (ts.isTypeQueryNode(curr.parent) && curr === curr.parent.exprName && ts.isAsExpression(curr.parent.parent) && curr.parent.parent.expression) {
224
+ const expression = getUnwrappedExpression(ts, curr.parent.parent.expression);
225
+ if (ts.isFunctionLike(expression)) yield expression;
226
+ curr = curr.parent.parent;
227
+ continue inner;
228
+ } else if (ts.isTypeQueryNode(curr.parent) && curr === curr.parent.exprName && ts.isPropertySignature(curr.parent.parent)) start = curr.parent.parent.name.getStart(sourceFile);
229
+ else continue outer;
230
+ break;
231
+ }
232
+ yield* forEachSignatureReference(ts, languageService, fileName, start, visited);
233
+ }
234
+ }
235
+ function getUnwrappedExpression(ts, node) {
236
+ while (ts.isParenthesizedExpression(node)) {
237
+ node = node.expression;
238
+ if (ts.isBinaryExpression(node) && node.operatorToken.kind === ts.SyntaxKind.CommaToken) node = node.right;
239
+ }
240
+ return node;
241
+ }
242
+ function* calculateTextChanges(ts, sourceFile, args, index, modifier) {
243
+ const spreadIndex = args.findIndex((arg) => ts.isSpreadElement(arg));
244
+ if (spreadIndex !== -1 && spreadIndex <= Math.max(index, index + modifier)) return;
245
+ if (modifier === 0 || modifier === Infinity) {
246
+ const from = index;
247
+ const to = modifier === Infinity ? args.length - 1 : Math.min(args.length - 1, index);
248
+ const [start, end] = from ? [args[from - 1].end, args[to].end] : [args[from].getStart(sourceFile), args[to + 1]?.getStart(sourceFile) ?? args[to].end];
249
+ yield {
250
+ span: {
251
+ start,
252
+ length: end - start
253
+ },
254
+ newText: ""
255
+ };
256
+ return;
257
+ }
258
+ for (let i = index; modifier === -1 ? i >= index + modifier : i <= index + modifier; i += modifier) yield {
259
+ span: {
260
+ start: args[i].getStart(sourceFile),
261
+ length: args[i].getWidth(sourceFile)
262
+ },
263
+ newText: args[i === index ? i + modifier : index].getText(sourceFile)
264
+ };
265
+ }
266
+
267
+ //#endregion
268
+ //#region src/index.ts
269
+ const plugin = (module) => {
270
+ const { typescript: ts } = module;
271
+ return { create(info) {
272
+ const methods = {};
273
+ for (const [key, method] of [
274
+ ["getApplicableRefactors", getApplicableRefactors],
275
+ ["getEditsForRefactor", getEditsForRefactor],
276
+ ["getDefinitionAndBoundSpan", getDefinitionAndBoundSpan]
277
+ ]) {
278
+ const original = info.languageService[key];
279
+ methods[key] = method(ts, info, original);
280
+ }
281
+ return new Proxy(info.languageService, { get(target, p, receiver) {
282
+ return methods[p] ?? Reflect.get(target, p, receiver);
283
+ } });
284
+ } };
285
+ };
286
+
287
+ //#endregion
288
+ module.exports = plugin;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dxup/vanilla",
3
3
  "type": "module",
4
- "version": "0.0.1",
4
+ "version": "0.0.2",
5
5
  "description": "TypeScript plugin for Vanilla JS",
6
6
  "author": "KazariEX",
7
7
  "license": "MIT",
@@ -15,9 +15,12 @@
15
15
  "files": [
16
16
  "dist"
17
17
  ],
18
+ "peerDependencies": {
19
+ "typescript": "*"
20
+ },
18
21
  "devDependencies": {
19
- "@dxup/shared": "",
20
- "typescript": "^5.9.3"
22
+ "typescript": "^5.9.3",
23
+ "@dxup/shared": "0.0.0"
21
24
  },
22
25
  "scripts": {
23
26
  "build": "tsdown",