detype 1.0.11 → 1.1.0
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.
- package/detype.js +1 -1
- package/dist/chunk-ESFGC2T7.js +252 -0
- package/dist/cli.js +54 -303
- package/dist/index.cjs +283 -0
- package/dist/index.d.cts +43 -0
- package/dist/index.js +8 -279
- package/package.json +25 -13
package/detype.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
2
|
+
import "./dist/cli";
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
// src/transform.ts
|
|
2
|
+
import {
|
|
3
|
+
transformAsync
|
|
4
|
+
} from "@babel/core";
|
|
5
|
+
import { format } from "prettier";
|
|
6
|
+
import {
|
|
7
|
+
parse as parseVueSfc
|
|
8
|
+
} from "@vuedx/compiler-sfc";
|
|
9
|
+
import { compileScript } from "@vue/compiler-sfc";
|
|
10
|
+
import {
|
|
11
|
+
traverse as traverseVueAst,
|
|
12
|
+
isSimpleExpressionNode as isVueSimpleExpressionNode,
|
|
13
|
+
isComponentNode as isVueComponentNode
|
|
14
|
+
} from "@vuedx/template-ast-types";
|
|
15
|
+
import babelTs from "@babel/preset-typescript";
|
|
16
|
+
import { shim } from "string.prototype.replaceall";
|
|
17
|
+
shim();
|
|
18
|
+
function getDefinePropsObject(content) {
|
|
19
|
+
const matched = /\sprops:\s*\{/m.exec(content);
|
|
20
|
+
if (matched) {
|
|
21
|
+
const startContentIndex = matched.index + matched[0].length - 1;
|
|
22
|
+
let leftBracketCount = 1;
|
|
23
|
+
let endContentIndex = startContentIndex + 1;
|
|
24
|
+
while (leftBracketCount) {
|
|
25
|
+
if (content.charAt(endContentIndex) === "{") {
|
|
26
|
+
leftBracketCount++;
|
|
27
|
+
} else if (content.charAt(endContentIndex) === "}") {
|
|
28
|
+
leftBracketCount--;
|
|
29
|
+
}
|
|
30
|
+
endContentIndex++;
|
|
31
|
+
}
|
|
32
|
+
return content.substring(startContentIndex, endContentIndex);
|
|
33
|
+
}
|
|
34
|
+
return "";
|
|
35
|
+
}
|
|
36
|
+
async function transform(code, fileName, options = {}) {
|
|
37
|
+
const { prettierOptions, ...removeTypeOptions } = options;
|
|
38
|
+
const originalCode = code;
|
|
39
|
+
const originalFileName = fileName;
|
|
40
|
+
let propsContent = "";
|
|
41
|
+
let emitsContent = "";
|
|
42
|
+
code = code.replaceAll("\r\n", "\n");
|
|
43
|
+
if (fileName.endsWith(".vue")) {
|
|
44
|
+
const parsedVue = parseVueSfc(code);
|
|
45
|
+
if (parsedVue.descriptor.script?.lang !== "ts" && parsedVue.descriptor.scriptSetup?.lang !== "ts") {
|
|
46
|
+
return originalCode;
|
|
47
|
+
}
|
|
48
|
+
let { script: script1, scriptSetup: script2 } = parsedVue.descriptor;
|
|
49
|
+
const isContainsDefinePropsType = script2?.content.match(/defineProps\s*</m);
|
|
50
|
+
const isContainsDefineEmitType = script2?.content.match(/defineEmits\s*</m);
|
|
51
|
+
if (isContainsDefinePropsType || isContainsDefineEmitType) {
|
|
52
|
+
const { content } = compileScript(parsedVue.descriptor, {
|
|
53
|
+
id: "xxxxxxx"
|
|
54
|
+
});
|
|
55
|
+
if (isContainsDefinePropsType) {
|
|
56
|
+
propsContent = getDefinePropsObject(content);
|
|
57
|
+
}
|
|
58
|
+
if (isContainsDefineEmitType) {
|
|
59
|
+
emitsContent = content.match(/\semits:\s(\[.*\]?)/m)?.[1] || "";
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (script1 && script2 && script1.loc.start.offset < script2.loc.start.offset) {
|
|
63
|
+
[script2, script1] = [script1, script2];
|
|
64
|
+
}
|
|
65
|
+
code = await removeTypesFromVueSfcScript(
|
|
66
|
+
code,
|
|
67
|
+
fileName,
|
|
68
|
+
script1,
|
|
69
|
+
parsedVue.descriptor.template?.ast,
|
|
70
|
+
removeTypeOptions
|
|
71
|
+
);
|
|
72
|
+
code = await removeTypesFromVueSfcScript(
|
|
73
|
+
code,
|
|
74
|
+
fileName,
|
|
75
|
+
script2,
|
|
76
|
+
parsedVue.descriptor.template?.ast,
|
|
77
|
+
removeTypeOptions
|
|
78
|
+
);
|
|
79
|
+
} else {
|
|
80
|
+
code = await removeTypes(code, fileName, removeTypeOptions);
|
|
81
|
+
}
|
|
82
|
+
if (propsContent) {
|
|
83
|
+
code = code.replace("defineProps(", (str) => `${str}${propsContent}`);
|
|
84
|
+
}
|
|
85
|
+
if (emitsContent) {
|
|
86
|
+
code = code.replace("defineEmits(", (str) => `${str}${emitsContent}`);
|
|
87
|
+
}
|
|
88
|
+
code = await format(code, {
|
|
89
|
+
...prettierOptions,
|
|
90
|
+
filepath: originalFileName
|
|
91
|
+
});
|
|
92
|
+
return code;
|
|
93
|
+
}
|
|
94
|
+
async function removeTypes(code, fileName, options) {
|
|
95
|
+
code = code.replace(
|
|
96
|
+
/\n\n+/g,
|
|
97
|
+
(match) => `
|
|
98
|
+
/* @detype: empty-line=${match.length} */
|
|
99
|
+
`
|
|
100
|
+
);
|
|
101
|
+
code = processMagicComments(code);
|
|
102
|
+
const removeComments = {
|
|
103
|
+
enter(p) {
|
|
104
|
+
if (!p.node.leadingComments) return;
|
|
105
|
+
for (let i = p.node.leadingComments.length - 1; i >= 0; i--) {
|
|
106
|
+
const comment = p.node.leadingComments[i];
|
|
107
|
+
if (code.slice(comment.end).match(/^\s*\n\s*\n/) || comment.value.includes("@detype: empty-line")) {
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
comment.value = "@detype: remove-me";
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
const babelConfig = {
|
|
115
|
+
filename: fileName,
|
|
116
|
+
retainLines: true,
|
|
117
|
+
plugins: [
|
|
118
|
+
// Plugin to remove leading comments attached to TypeScript-only constructs
|
|
119
|
+
{
|
|
120
|
+
name: "detype-comment-remover",
|
|
121
|
+
visitor: {
|
|
122
|
+
TSTypeAliasDeclaration: removeComments,
|
|
123
|
+
TSInterfaceDeclaration: removeComments,
|
|
124
|
+
TSDeclareFunction: removeComments,
|
|
125
|
+
TSDeclareMethod: removeComments,
|
|
126
|
+
TSImportType: removeComments
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
].filter(Boolean),
|
|
130
|
+
presets: [babelTs],
|
|
131
|
+
generatorOpts: {
|
|
132
|
+
shouldPrintComment: (comment) => comment !== "@detype: remove-me" && (!options.removeTsComments || !comment.match(/^\s*(@ts-ignore|@ts-expect-error)/))
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
if (options.customizeBabelConfig) {
|
|
136
|
+
options.customizeBabelConfig(babelConfig);
|
|
137
|
+
}
|
|
138
|
+
const babelOutput = await transformAsync(code, babelConfig);
|
|
139
|
+
if (!babelOutput || babelOutput.code === void 0 || babelOutput.code === null) {
|
|
140
|
+
throw new Error("Babel error");
|
|
141
|
+
}
|
|
142
|
+
return babelOutput.code.replaceAll(/\n\n*/g, "\n").replace(
|
|
143
|
+
/\/\* @detype: empty-line=([0-9]+) \*\//g,
|
|
144
|
+
(_match, p1) => `
|
|
145
|
+
`.repeat(p1 - 2)
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
async function removeTypesFromVueSfcScript(code, fileName, script, templateAst, options) {
|
|
149
|
+
if (script === null || script.lang !== "ts") return code;
|
|
150
|
+
if (script.setup && templateAst) {
|
|
151
|
+
const expressions = /* @__PURE__ */ new Set();
|
|
152
|
+
traverseVueAst(templateAst, {
|
|
153
|
+
enter(node) {
|
|
154
|
+
if (isVueSimpleExpressionNode(node) && !node.isStatic) {
|
|
155
|
+
expressions.add(`[${node.content}]`);
|
|
156
|
+
} else if (isVueComponentNode(node)) {
|
|
157
|
+
expressions.add(`[${node.tag}]`);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
script.content += "/* @detype: remove-after-this */" + [...expressions].join(";");
|
|
162
|
+
}
|
|
163
|
+
let scriptCode = await removeTypes(script.content, fileName + ".ts", options);
|
|
164
|
+
const removeAfterIndex = scriptCode.indexOf(
|
|
165
|
+
"/* @detype: remove-after-this */"
|
|
166
|
+
);
|
|
167
|
+
if (removeAfterIndex >= 0) {
|
|
168
|
+
scriptCode = scriptCode.slice(0, removeAfterIndex);
|
|
169
|
+
}
|
|
170
|
+
let before = code.slice(0, script.loc.start.offset);
|
|
171
|
+
const after = code.slice(script.loc.end.offset);
|
|
172
|
+
const matches = before.match(/\blang\s*=\s*["']ts["']/);
|
|
173
|
+
if (matches) {
|
|
174
|
+
const lastMatch = matches[matches.length - 1];
|
|
175
|
+
const lastMatchIndex = before.lastIndexOf(lastMatch);
|
|
176
|
+
before = before.slice(0, lastMatchIndex) + before.slice(lastMatchIndex + lastMatch.length);
|
|
177
|
+
}
|
|
178
|
+
return before + scriptCode + after;
|
|
179
|
+
}
|
|
180
|
+
function processMagicComments(input) {
|
|
181
|
+
const REPLACE_COMMENT = "// @detype: replace\n";
|
|
182
|
+
const WITH_COMMENT = "// @detype: with\n";
|
|
183
|
+
const END_COMMENT = "// @detype: end\n";
|
|
184
|
+
let start = input.indexOf(REPLACE_COMMENT);
|
|
185
|
+
while (start >= 0) {
|
|
186
|
+
const middle = input.indexOf(WITH_COMMENT, start);
|
|
187
|
+
if (middle < 0) return input;
|
|
188
|
+
const middleEnd = middle + WITH_COMMENT.length;
|
|
189
|
+
const end = input.indexOf(END_COMMENT, middleEnd);
|
|
190
|
+
if (end < 0) return input;
|
|
191
|
+
const endEnd = end + END_COMMENT.length;
|
|
192
|
+
const before = input.slice(0, start);
|
|
193
|
+
const newText = input.slice(middleEnd, end).replaceAll(/^\s*\/\//gm, "");
|
|
194
|
+
const after = input.slice(endEnd);
|
|
195
|
+
input = before + newText + after;
|
|
196
|
+
start = input.indexOf(REPLACE_COMMENT, before.length + newText.length);
|
|
197
|
+
}
|
|
198
|
+
return input;
|
|
199
|
+
}
|
|
200
|
+
async function removeMagicComments(code, fileName, prettierOptions) {
|
|
201
|
+
const REPLACE_COMMENT = "// @detype: replace\n";
|
|
202
|
+
const WITH_COMMENT = "// @detype: with\n";
|
|
203
|
+
const END_COMMENT = "// @detype: end\n";
|
|
204
|
+
let start = code.indexOf(REPLACE_COMMENT);
|
|
205
|
+
let startEnd = start + REPLACE_COMMENT.length;
|
|
206
|
+
while (start >= 0) {
|
|
207
|
+
const middle = code.indexOf(WITH_COMMENT, start);
|
|
208
|
+
if (middle < 0) return code;
|
|
209
|
+
const middleEnd = middle + WITH_COMMENT.length;
|
|
210
|
+
const end = code.indexOf(END_COMMENT, middleEnd);
|
|
211
|
+
if (end < 0) return code;
|
|
212
|
+
const endEnd = end + END_COMMENT.length;
|
|
213
|
+
const before = code.slice(0, start);
|
|
214
|
+
const keptText = code.slice(startEnd, middle);
|
|
215
|
+
const after = code.slice(endEnd);
|
|
216
|
+
code = before + keptText + after;
|
|
217
|
+
start = code.indexOf(REPLACE_COMMENT, before.length + keptText.length);
|
|
218
|
+
startEnd = start + REPLACE_COMMENT.length;
|
|
219
|
+
}
|
|
220
|
+
code = await format(code, {
|
|
221
|
+
...prettierOptions,
|
|
222
|
+
filepath: fileName
|
|
223
|
+
});
|
|
224
|
+
return code;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// src/transformFile.ts
|
|
228
|
+
import fs from "node:fs";
|
|
229
|
+
import { resolveConfig } from "prettier";
|
|
230
|
+
var { readFile, writeFile } = fs.promises;
|
|
231
|
+
async function transformFile(inputFileName, outputFileName, options = {}) {
|
|
232
|
+
const code = await readFile(inputFileName, "utf-8");
|
|
233
|
+
const prettierOptions = await resolveConfig(inputFileName);
|
|
234
|
+
const output = await transform(code, inputFileName, {
|
|
235
|
+
prettierOptions,
|
|
236
|
+
...options
|
|
237
|
+
});
|
|
238
|
+
await writeFile(outputFileName, output, "utf-8");
|
|
239
|
+
}
|
|
240
|
+
async function removeMagicCommentsFromFile(inputFileName, outputFileName) {
|
|
241
|
+
const code = await readFile(inputFileName, "utf-8");
|
|
242
|
+
const prettierConfig = await resolveConfig(inputFileName);
|
|
243
|
+
const output = await removeMagicComments(code, inputFileName, prettierConfig);
|
|
244
|
+
await writeFile(outputFileName, output, "utf-8");
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
export {
|
|
248
|
+
transform,
|
|
249
|
+
removeMagicComments,
|
|
250
|
+
transformFile,
|
|
251
|
+
removeMagicCommentsFromFile
|
|
252
|
+
};
|
package/dist/cli.js
CHANGED
|
@@ -1,280 +1,31 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __copyProps = (to, from, except, desc) => {
|
|
9
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
-
for (let key of __getOwnPropNames(from))
|
|
11
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
-
}
|
|
14
|
-
return to;
|
|
15
|
-
};
|
|
16
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
-
mod
|
|
23
|
-
));
|
|
24
|
-
|
|
25
|
-
// src/cli-lib.ts
|
|
26
|
-
var import_node_fs2 = __toESM(require("fs"));
|
|
27
|
-
var import_node_path = __toESM(require("path"));
|
|
28
|
-
|
|
29
|
-
// src/transformFile.ts
|
|
30
|
-
var import_node_fs = __toESM(require("fs"));
|
|
31
|
-
|
|
32
|
-
// src/transform.ts
|
|
33
|
-
var import_core = require("@babel/core");
|
|
34
|
-
var import_prettier = require("prettier");
|
|
35
|
-
var import_compiler_sfc = require("@vuedx/compiler-sfc");
|
|
36
|
-
var import_compiler_sfc2 = require("@vue/compiler-sfc");
|
|
37
|
-
var import_template_ast_types = require("@vuedx/template-ast-types");
|
|
38
|
-
var import_preset_typescript = __toESM(require("@babel/preset-typescript"));
|
|
39
|
-
var import_string_prototype = require("string.prototype.replaceall");
|
|
40
|
-
(0, import_string_prototype.shim)();
|
|
41
|
-
function getDefinePropsObject(content) {
|
|
42
|
-
const matched = /\sprops:\s*\{/m.exec(content);
|
|
43
|
-
if (matched) {
|
|
44
|
-
const startContentIndex = matched.index + matched[0].length - 1;
|
|
45
|
-
let leftBracketCount = 1;
|
|
46
|
-
let endContentIndex = startContentIndex + 1;
|
|
47
|
-
while (leftBracketCount) {
|
|
48
|
-
if (content.charAt(endContentIndex) === "{") {
|
|
49
|
-
leftBracketCount++;
|
|
50
|
-
} else if (content.charAt(endContentIndex) === "}") {
|
|
51
|
-
leftBracketCount--;
|
|
52
|
-
}
|
|
53
|
-
endContentIndex++;
|
|
54
|
-
}
|
|
55
|
-
return content.substring(startContentIndex, endContentIndex);
|
|
56
|
-
}
|
|
57
|
-
return "";
|
|
58
|
-
}
|
|
59
|
-
async function transform(code, fileName, options = {}) {
|
|
60
|
-
const { prettierOptions, ...removeTypeOptions } = options;
|
|
61
|
-
const originalCode = code;
|
|
62
|
-
const originalFileName = fileName;
|
|
63
|
-
let propsContent = "";
|
|
64
|
-
let emitsContent = "";
|
|
65
|
-
code = code.replaceAll("\r\n", "\n");
|
|
66
|
-
if (fileName.endsWith(".vue")) {
|
|
67
|
-
const parsedVue = (0, import_compiler_sfc.parse)(code);
|
|
68
|
-
if (parsedVue.descriptor.script?.lang !== "ts" && parsedVue.descriptor.scriptSetup?.lang !== "ts") {
|
|
69
|
-
return originalCode;
|
|
70
|
-
}
|
|
71
|
-
let { script: script1, scriptSetup: script2 } = parsedVue.descriptor;
|
|
72
|
-
const isContainsDefinePropsType = script2?.content.match(/defineProps\s*</m);
|
|
73
|
-
const isContainsDefineEmitType = script2?.content.match(/defineEmits\s*</m);
|
|
74
|
-
if (isContainsDefinePropsType || isContainsDefineEmitType) {
|
|
75
|
-
const { content } = (0, import_compiler_sfc2.compileScript)(parsedVue.descriptor, {
|
|
76
|
-
id: "xxxxxxx"
|
|
77
|
-
});
|
|
78
|
-
if (isContainsDefinePropsType) {
|
|
79
|
-
propsContent = getDefinePropsObject(content);
|
|
80
|
-
}
|
|
81
|
-
if (isContainsDefineEmitType) {
|
|
82
|
-
emitsContent = content.match(/\semits:\s(\[.*\]?)/m)?.[1] || "";
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
if (script1 && script2 && script1.loc.start.offset < script2.loc.start.offset) {
|
|
86
|
-
[script2, script1] = [script1, script2];
|
|
87
|
-
}
|
|
88
|
-
code = await removeTypesFromVueSfcScript(
|
|
89
|
-
code,
|
|
90
|
-
fileName,
|
|
91
|
-
script1,
|
|
92
|
-
parsedVue.descriptor.template?.ast,
|
|
93
|
-
removeTypeOptions
|
|
94
|
-
);
|
|
95
|
-
code = await removeTypesFromVueSfcScript(
|
|
96
|
-
code,
|
|
97
|
-
fileName,
|
|
98
|
-
script2,
|
|
99
|
-
parsedVue.descriptor.template?.ast,
|
|
100
|
-
removeTypeOptions
|
|
101
|
-
);
|
|
102
|
-
} else {
|
|
103
|
-
code = await removeTypes(code, fileName, removeTypeOptions);
|
|
104
|
-
}
|
|
105
|
-
if (propsContent) {
|
|
106
|
-
code = code.replace("defineProps(", (str) => `${str}${propsContent}`);
|
|
107
|
-
}
|
|
108
|
-
if (emitsContent) {
|
|
109
|
-
code = code.replace("defineEmits(", (str) => `${str}${emitsContent}`);
|
|
110
|
-
}
|
|
111
|
-
code = await (0, import_prettier.format)(code, {
|
|
112
|
-
...prettierOptions,
|
|
113
|
-
filepath: originalFileName
|
|
114
|
-
});
|
|
115
|
-
return code;
|
|
116
|
-
}
|
|
117
|
-
async function removeTypes(code, fileName, options) {
|
|
118
|
-
code = code.replace(
|
|
119
|
-
/\n\n+/g,
|
|
120
|
-
(match) => `
|
|
121
|
-
/* @detype: empty-line=${match.length} */
|
|
122
|
-
`
|
|
123
|
-
);
|
|
124
|
-
code = processMagicComments(code);
|
|
125
|
-
const removeComments = {
|
|
126
|
-
enter(p) {
|
|
127
|
-
if (!p.node.leadingComments) return;
|
|
128
|
-
for (let i = p.node.leadingComments.length - 1; i >= 0; i--) {
|
|
129
|
-
const comment = p.node.leadingComments[i];
|
|
130
|
-
if (code.slice(comment.end).match(/^\s*\n\s*\n/) || comment.value.includes("@detype: empty-line")) {
|
|
131
|
-
break;
|
|
132
|
-
}
|
|
133
|
-
comment.value = "@detype: remove-me";
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
const babelConfig = {
|
|
138
|
-
filename: fileName,
|
|
139
|
-
retainLines: true,
|
|
140
|
-
plugins: [
|
|
141
|
-
// Plugin to remove leading comments attached to TypeScript-only constructs
|
|
142
|
-
{
|
|
143
|
-
name: "detype-comment-remover",
|
|
144
|
-
visitor: {
|
|
145
|
-
TSTypeAliasDeclaration: removeComments,
|
|
146
|
-
TSInterfaceDeclaration: removeComments,
|
|
147
|
-
TSDeclareFunction: removeComments,
|
|
148
|
-
TSDeclareMethod: removeComments,
|
|
149
|
-
TSImportType: removeComments
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
].filter(Boolean),
|
|
153
|
-
presets: [import_preset_typescript.default],
|
|
154
|
-
generatorOpts: {
|
|
155
|
-
shouldPrintComment: (comment) => comment !== "@detype: remove-me" && (!options.removeTsComments || !comment.match(/^\s*(@ts-ignore|@ts-expect-error)/))
|
|
156
|
-
}
|
|
157
|
-
};
|
|
158
|
-
if (options.customizeBabelConfig) {
|
|
159
|
-
options.customizeBabelConfig(babelConfig);
|
|
160
|
-
}
|
|
161
|
-
const babelOutput = await (0, import_core.transformAsync)(code, babelConfig);
|
|
162
|
-
if (!babelOutput || babelOutput.code === void 0 || babelOutput.code === null) {
|
|
163
|
-
throw new Error("Babel error");
|
|
164
|
-
}
|
|
165
|
-
return babelOutput.code.replaceAll(/\n\n*/g, "\n").replace(
|
|
166
|
-
/\/\* @detype: empty-line=([0-9]+) \*\//g,
|
|
167
|
-
(_match, p1) => `
|
|
168
|
-
`.repeat(p1 - 2)
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
async function removeTypesFromVueSfcScript(code, fileName, script, templateAst, options) {
|
|
172
|
-
if (script === null || script.lang !== "ts") return code;
|
|
173
|
-
if (script.setup && templateAst) {
|
|
174
|
-
const expressions = /* @__PURE__ */ new Set();
|
|
175
|
-
(0, import_template_ast_types.traverse)(templateAst, {
|
|
176
|
-
enter(node) {
|
|
177
|
-
if ((0, import_template_ast_types.isSimpleExpressionNode)(node) && !node.isStatic) {
|
|
178
|
-
expressions.add(`[${node.content}]`);
|
|
179
|
-
} else if ((0, import_template_ast_types.isComponentNode)(node)) {
|
|
180
|
-
expressions.add(`[${node.tag}]`);
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
});
|
|
184
|
-
script.content += "/* @detype: remove-after-this */" + [...expressions].join(";");
|
|
185
|
-
}
|
|
186
|
-
let scriptCode = await removeTypes(script.content, fileName + ".ts", options);
|
|
187
|
-
const removeAfterIndex = scriptCode.indexOf(
|
|
188
|
-
"/* @detype: remove-after-this */"
|
|
189
|
-
);
|
|
190
|
-
if (removeAfterIndex >= 0) {
|
|
191
|
-
scriptCode = scriptCode.slice(0, removeAfterIndex);
|
|
192
|
-
}
|
|
193
|
-
let before = code.slice(0, script.loc.start.offset);
|
|
194
|
-
const after = code.slice(script.loc.end.offset);
|
|
195
|
-
const matches = before.match(/\blang\s*=\s*["']ts["']/);
|
|
196
|
-
if (matches) {
|
|
197
|
-
const lastMatch = matches[matches.length - 1];
|
|
198
|
-
const lastMatchIndex = before.lastIndexOf(lastMatch);
|
|
199
|
-
before = before.slice(0, lastMatchIndex) + before.slice(lastMatchIndex + lastMatch.length);
|
|
200
|
-
}
|
|
201
|
-
return before + scriptCode + after;
|
|
202
|
-
}
|
|
203
|
-
function processMagicComments(input) {
|
|
204
|
-
const REPLACE_COMMENT = "// @detype: replace\n";
|
|
205
|
-
const WITH_COMMENT = "// @detype: with\n";
|
|
206
|
-
const END_COMMENT = "// @detype: end\n";
|
|
207
|
-
let start = input.indexOf(REPLACE_COMMENT);
|
|
208
|
-
while (start >= 0) {
|
|
209
|
-
const middle = input.indexOf(WITH_COMMENT, start);
|
|
210
|
-
if (middle < 0) return input;
|
|
211
|
-
const middleEnd = middle + WITH_COMMENT.length;
|
|
212
|
-
const end = input.indexOf(END_COMMENT, middleEnd);
|
|
213
|
-
if (end < 0) return input;
|
|
214
|
-
const endEnd = end + END_COMMENT.length;
|
|
215
|
-
const before = input.slice(0, start);
|
|
216
|
-
const newText = input.slice(middleEnd, end).replaceAll(/^\s*\/\//gm, "");
|
|
217
|
-
const after = input.slice(endEnd);
|
|
218
|
-
input = before + newText + after;
|
|
219
|
-
start = input.indexOf(REPLACE_COMMENT, before.length + newText.length);
|
|
220
|
-
}
|
|
221
|
-
return input;
|
|
222
|
-
}
|
|
223
|
-
async function removeMagicComments(code, fileName, prettierOptions) {
|
|
224
|
-
const REPLACE_COMMENT = "// @detype: replace\n";
|
|
225
|
-
const WITH_COMMENT = "// @detype: with\n";
|
|
226
|
-
const END_COMMENT = "// @detype: end\n";
|
|
227
|
-
let start = code.indexOf(REPLACE_COMMENT);
|
|
228
|
-
let startEnd = start + REPLACE_COMMENT.length;
|
|
229
|
-
while (start >= 0) {
|
|
230
|
-
const middle = code.indexOf(WITH_COMMENT, start);
|
|
231
|
-
if (middle < 0) return code;
|
|
232
|
-
const middleEnd = middle + WITH_COMMENT.length;
|
|
233
|
-
const end = code.indexOf(END_COMMENT, middleEnd);
|
|
234
|
-
if (end < 0) return code;
|
|
235
|
-
const endEnd = end + END_COMMENT.length;
|
|
236
|
-
const before = code.slice(0, start);
|
|
237
|
-
const keptText = code.slice(startEnd, middle);
|
|
238
|
-
const after = code.slice(endEnd);
|
|
239
|
-
code = before + keptText + after;
|
|
240
|
-
start = code.indexOf(REPLACE_COMMENT, before.length + keptText.length);
|
|
241
|
-
startEnd = start + REPLACE_COMMENT.length;
|
|
242
|
-
}
|
|
243
|
-
code = await (0, import_prettier.format)(code, {
|
|
244
|
-
...prettierOptions,
|
|
245
|
-
filepath: fileName
|
|
246
|
-
});
|
|
247
|
-
return code;
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
// src/transformFile.ts
|
|
251
|
-
var import_prettier2 = require("prettier");
|
|
252
|
-
var { readFile, writeFile } = import_node_fs.default.promises;
|
|
253
|
-
async function transformFile(inputFileName, outputFileName, options = {}) {
|
|
254
|
-
const code = await readFile(inputFileName, "utf-8");
|
|
255
|
-
const prettierOptions = await (0, import_prettier2.resolveConfig)(inputFileName);
|
|
256
|
-
const output = await transform(code, inputFileName, {
|
|
257
|
-
prettierOptions,
|
|
258
|
-
...options
|
|
259
|
-
});
|
|
260
|
-
await writeFile(outputFileName, output, "utf-8");
|
|
261
|
-
}
|
|
262
|
-
async function removeMagicCommentsFromFile(inputFileName, outputFileName) {
|
|
263
|
-
const code = await readFile(inputFileName, "utf-8");
|
|
264
|
-
const prettierConfig = await (0, import_prettier2.resolveConfig)(inputFileName);
|
|
265
|
-
const output = await removeMagicComments(code, inputFileName, prettierConfig);
|
|
266
|
-
await writeFile(outputFileName, output, "utf-8");
|
|
267
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
removeMagicCommentsFromFile,
|
|
3
|
+
transformFile
|
|
4
|
+
} from "./chunk-ESFGC2T7.js";
|
|
268
5
|
|
|
269
6
|
// src/cli-lib.ts
|
|
270
|
-
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import fastGlob from "fast-glob";
|
|
271
10
|
|
|
272
11
|
// package.json
|
|
273
12
|
var package_default = {
|
|
274
13
|
name: "detype",
|
|
275
|
-
version: "1.0
|
|
14
|
+
version: "1.1.0",
|
|
276
15
|
description: "Removes TypeScript type annotations but keeps the formatting",
|
|
277
|
-
|
|
16
|
+
type: "module",
|
|
17
|
+
exports: {
|
|
18
|
+
".": {
|
|
19
|
+
import: {
|
|
20
|
+
types: "./dist/index.d.ts",
|
|
21
|
+
default: "./dist/index.js"
|
|
22
|
+
},
|
|
23
|
+
require: {
|
|
24
|
+
types: "./dist/index.d.cts",
|
|
25
|
+
default: "./dist/index.cjs"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
},
|
|
278
29
|
bin: "detype.js",
|
|
279
30
|
scripts: {
|
|
280
31
|
prepack: "rimraf dist && pnpm build",
|
|
@@ -293,11 +44,11 @@ var package_default = {
|
|
|
293
44
|
"index.d.ts"
|
|
294
45
|
],
|
|
295
46
|
dependencies: {
|
|
296
|
-
"@babel/core": "^7.
|
|
297
|
-
"@babel/preset-typescript": "^7.
|
|
298
|
-
"@babel/traverse": "^7.25.
|
|
299
|
-
"@vue/compiler-dom": "^3.5.
|
|
300
|
-
"@vue/compiler-sfc": "^3.5.
|
|
47
|
+
"@babel/core": "^7.26.0",
|
|
48
|
+
"@babel/preset-typescript": "^7.26.0",
|
|
49
|
+
"@babel/traverse": "^7.25.9",
|
|
50
|
+
"@vue/compiler-dom": "^3.5.12",
|
|
51
|
+
"@vue/compiler-sfc": "^3.5.12",
|
|
301
52
|
"@vuedx/compiler-sfc": "0.7.1",
|
|
302
53
|
"@vuedx/template-ast-types": "0.7.1",
|
|
303
54
|
"fast-glob": "^3.3.2",
|
|
@@ -305,15 +56,15 @@ var package_default = {
|
|
|
305
56
|
"string.prototype.replaceall": "^1.0.10"
|
|
306
57
|
},
|
|
307
58
|
devDependencies: {
|
|
308
|
-
"@cyco130/eslint-config": "^
|
|
59
|
+
"@cyco130/eslint-config": "^5.0.0",
|
|
309
60
|
"@types/babel__core": "^7.20.5",
|
|
310
61
|
"@types/babel__traverse": "^7.20.6",
|
|
311
|
-
"@types/node": "22.
|
|
312
|
-
eslint: "^
|
|
62
|
+
"@types/node": "22.8.6",
|
|
63
|
+
eslint: "^9.14.0",
|
|
313
64
|
rimraf: "^6.0.1",
|
|
314
|
-
tsup: "^8.
|
|
315
|
-
typescript: "^5.
|
|
316
|
-
vitest: "2.
|
|
65
|
+
tsup: "^8.3.5",
|
|
66
|
+
typescript: "^5.6.3",
|
|
67
|
+
vitest: "2.1.4"
|
|
317
68
|
},
|
|
318
69
|
repository: {
|
|
319
70
|
type: "git",
|
|
@@ -334,7 +85,7 @@ var package_default = {
|
|
|
334
85
|
};
|
|
335
86
|
|
|
336
87
|
// src/cli-lib.ts
|
|
337
|
-
var { stat, mkdir } =
|
|
88
|
+
var { stat, mkdir } = fs.promises;
|
|
338
89
|
async function cli(...args2) {
|
|
339
90
|
let dashDash = false;
|
|
340
91
|
const params = [];
|
|
@@ -387,24 +138,24 @@ async function cli(...args2) {
|
|
|
387
138
|
printUsage();
|
|
388
139
|
return false;
|
|
389
140
|
}
|
|
390
|
-
const files = (await (
|
|
391
|
-
const dirs = [...new Set(files.map((file) =>
|
|
392
|
-
await mkdir(
|
|
141
|
+
const files = (await fastGlob(unixify(input + "/**/*.{ts,tsx,vue}"))).filter((file) => !file.endsWith(".d.ts"));
|
|
142
|
+
const dirs = [...new Set(files.map((file) => path.dirname(file)))].sort();
|
|
143
|
+
await mkdir(path.normalize(output), { recursive: true });
|
|
393
144
|
for (const dir of dirs) {
|
|
394
|
-
const outDir =
|
|
145
|
+
const outDir = path.join(output, path.relative(input, dir));
|
|
395
146
|
if (outDir === output) continue;
|
|
396
|
-
await mkdir(
|
|
147
|
+
await mkdir(path.normalize(outDir), { recursive: true });
|
|
397
148
|
}
|
|
398
149
|
for (const file of files) {
|
|
399
|
-
const inputDir =
|
|
400
|
-
const outputName = inferName(file,
|
|
150
|
+
const inputDir = path.dirname(path.relative(input, file));
|
|
151
|
+
const outputName = inferName(file, path.join(output, inputDir));
|
|
401
152
|
if (removeMagic) {
|
|
402
153
|
await removeMagicCommentsFromFile(
|
|
403
|
-
|
|
404
|
-
|
|
154
|
+
path.normalize(file),
|
|
155
|
+
path.normalize(outputName)
|
|
405
156
|
);
|
|
406
157
|
} else {
|
|
407
|
-
await transformFile(
|
|
158
|
+
await transformFile(path.normalize(file), path.normalize(outputName), {
|
|
408
159
|
removeTsComments
|
|
409
160
|
});
|
|
410
161
|
}
|
|
@@ -434,32 +185,32 @@ async function cli(...args2) {
|
|
|
434
185
|
}
|
|
435
186
|
output = inferName(input);
|
|
436
187
|
}
|
|
437
|
-
const outputDir =
|
|
188
|
+
const outputDir = path.dirname(output);
|
|
438
189
|
if (outputDir) {
|
|
439
|
-
await mkdir(
|
|
190
|
+
await mkdir(path.normalize(outputDir), { recursive: true });
|
|
440
191
|
}
|
|
441
192
|
if (removeMagic) {
|
|
442
193
|
await removeMagicCommentsFromFile(
|
|
443
|
-
|
|
444
|
-
|
|
194
|
+
path.normalize(input),
|
|
195
|
+
path.normalize(output)
|
|
445
196
|
);
|
|
446
197
|
} else {
|
|
447
|
-
await transformFile(
|
|
198
|
+
await transformFile(path.normalize(input), path.normalize(output), {
|
|
448
199
|
removeTsComments
|
|
449
200
|
});
|
|
450
201
|
}
|
|
451
202
|
return true;
|
|
452
203
|
function inferName(input2, outputDir2) {
|
|
453
204
|
let output2;
|
|
454
|
-
const { dir, name, ext } =
|
|
205
|
+
const { dir, name, ext } = path.parse(input2);
|
|
455
206
|
if (removeMagic) {
|
|
456
|
-
output2 =
|
|
207
|
+
output2 = path.join(outputDir2 ?? dir, `${name}${ext}`);
|
|
457
208
|
} else if (ext === ".ts") {
|
|
458
|
-
output2 =
|
|
209
|
+
output2 = path.join(outputDir2 ?? dir, name + ".js");
|
|
459
210
|
} else if (ext === ".tsx") {
|
|
460
|
-
output2 =
|
|
211
|
+
output2 = path.join(outputDir2 ?? dir, name + ".jsx");
|
|
461
212
|
} else if (ext === ".vue") {
|
|
462
|
-
output2 =
|
|
213
|
+
output2 = path.join(outputDir2 ?? dir, name + ".vue");
|
|
463
214
|
} else {
|
|
464
215
|
throw new Error(`Unknwon file extension ${input2}`);
|
|
465
216
|
}
|
|
@@ -493,7 +244,7 @@ var USAGE = `Usage:
|
|
|
493
244
|
Print this help and exit`;
|
|
494
245
|
var VERSION = package_default.version;
|
|
495
246
|
function unixify(name) {
|
|
496
|
-
return name.replaceAll(
|
|
247
|
+
return name.replaceAll(path.sep, "/");
|
|
497
248
|
}
|
|
498
249
|
|
|
499
250
|
// src/cli.ts
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
removeMagicComments: () => removeMagicComments,
|
|
34
|
+
removeMagicCommentsFromFile: () => removeMagicCommentsFromFile,
|
|
35
|
+
transform: () => transform,
|
|
36
|
+
transformFile: () => transformFile
|
|
37
|
+
});
|
|
38
|
+
module.exports = __toCommonJS(src_exports);
|
|
39
|
+
|
|
40
|
+
// src/transform.ts
|
|
41
|
+
var import_core = require("@babel/core");
|
|
42
|
+
var import_prettier = require("prettier");
|
|
43
|
+
var import_compiler_sfc = require("@vuedx/compiler-sfc");
|
|
44
|
+
var import_compiler_sfc2 = require("@vue/compiler-sfc");
|
|
45
|
+
var import_template_ast_types = require("@vuedx/template-ast-types");
|
|
46
|
+
var import_preset_typescript = __toESM(require("@babel/preset-typescript"), 1);
|
|
47
|
+
var import_string_prototype = require("string.prototype.replaceall");
|
|
48
|
+
(0, import_string_prototype.shim)();
|
|
49
|
+
function getDefinePropsObject(content) {
|
|
50
|
+
const matched = /\sprops:\s*\{/m.exec(content);
|
|
51
|
+
if (matched) {
|
|
52
|
+
const startContentIndex = matched.index + matched[0].length - 1;
|
|
53
|
+
let leftBracketCount = 1;
|
|
54
|
+
let endContentIndex = startContentIndex + 1;
|
|
55
|
+
while (leftBracketCount) {
|
|
56
|
+
if (content.charAt(endContentIndex) === "{") {
|
|
57
|
+
leftBracketCount++;
|
|
58
|
+
} else if (content.charAt(endContentIndex) === "}") {
|
|
59
|
+
leftBracketCount--;
|
|
60
|
+
}
|
|
61
|
+
endContentIndex++;
|
|
62
|
+
}
|
|
63
|
+
return content.substring(startContentIndex, endContentIndex);
|
|
64
|
+
}
|
|
65
|
+
return "";
|
|
66
|
+
}
|
|
67
|
+
async function transform(code, fileName, options = {}) {
|
|
68
|
+
const { prettierOptions, ...removeTypeOptions } = options;
|
|
69
|
+
const originalCode = code;
|
|
70
|
+
const originalFileName = fileName;
|
|
71
|
+
let propsContent = "";
|
|
72
|
+
let emitsContent = "";
|
|
73
|
+
code = code.replaceAll("\r\n", "\n");
|
|
74
|
+
if (fileName.endsWith(".vue")) {
|
|
75
|
+
const parsedVue = (0, import_compiler_sfc.parse)(code);
|
|
76
|
+
if (parsedVue.descriptor.script?.lang !== "ts" && parsedVue.descriptor.scriptSetup?.lang !== "ts") {
|
|
77
|
+
return originalCode;
|
|
78
|
+
}
|
|
79
|
+
let { script: script1, scriptSetup: script2 } = parsedVue.descriptor;
|
|
80
|
+
const isContainsDefinePropsType = script2?.content.match(/defineProps\s*</m);
|
|
81
|
+
const isContainsDefineEmitType = script2?.content.match(/defineEmits\s*</m);
|
|
82
|
+
if (isContainsDefinePropsType || isContainsDefineEmitType) {
|
|
83
|
+
const { content } = (0, import_compiler_sfc2.compileScript)(parsedVue.descriptor, {
|
|
84
|
+
id: "xxxxxxx"
|
|
85
|
+
});
|
|
86
|
+
if (isContainsDefinePropsType) {
|
|
87
|
+
propsContent = getDefinePropsObject(content);
|
|
88
|
+
}
|
|
89
|
+
if (isContainsDefineEmitType) {
|
|
90
|
+
emitsContent = content.match(/\semits:\s(\[.*\]?)/m)?.[1] || "";
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (script1 && script2 && script1.loc.start.offset < script2.loc.start.offset) {
|
|
94
|
+
[script2, script1] = [script1, script2];
|
|
95
|
+
}
|
|
96
|
+
code = await removeTypesFromVueSfcScript(
|
|
97
|
+
code,
|
|
98
|
+
fileName,
|
|
99
|
+
script1,
|
|
100
|
+
parsedVue.descriptor.template?.ast,
|
|
101
|
+
removeTypeOptions
|
|
102
|
+
);
|
|
103
|
+
code = await removeTypesFromVueSfcScript(
|
|
104
|
+
code,
|
|
105
|
+
fileName,
|
|
106
|
+
script2,
|
|
107
|
+
parsedVue.descriptor.template?.ast,
|
|
108
|
+
removeTypeOptions
|
|
109
|
+
);
|
|
110
|
+
} else {
|
|
111
|
+
code = await removeTypes(code, fileName, removeTypeOptions);
|
|
112
|
+
}
|
|
113
|
+
if (propsContent) {
|
|
114
|
+
code = code.replace("defineProps(", (str) => `${str}${propsContent}`);
|
|
115
|
+
}
|
|
116
|
+
if (emitsContent) {
|
|
117
|
+
code = code.replace("defineEmits(", (str) => `${str}${emitsContent}`);
|
|
118
|
+
}
|
|
119
|
+
code = await (0, import_prettier.format)(code, {
|
|
120
|
+
...prettierOptions,
|
|
121
|
+
filepath: originalFileName
|
|
122
|
+
});
|
|
123
|
+
return code;
|
|
124
|
+
}
|
|
125
|
+
async function removeTypes(code, fileName, options) {
|
|
126
|
+
code = code.replace(
|
|
127
|
+
/\n\n+/g,
|
|
128
|
+
(match) => `
|
|
129
|
+
/* @detype: empty-line=${match.length} */
|
|
130
|
+
`
|
|
131
|
+
);
|
|
132
|
+
code = processMagicComments(code);
|
|
133
|
+
const removeComments = {
|
|
134
|
+
enter(p) {
|
|
135
|
+
if (!p.node.leadingComments) return;
|
|
136
|
+
for (let i = p.node.leadingComments.length - 1; i >= 0; i--) {
|
|
137
|
+
const comment = p.node.leadingComments[i];
|
|
138
|
+
if (code.slice(comment.end).match(/^\s*\n\s*\n/) || comment.value.includes("@detype: empty-line")) {
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
comment.value = "@detype: remove-me";
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
const babelConfig = {
|
|
146
|
+
filename: fileName,
|
|
147
|
+
retainLines: true,
|
|
148
|
+
plugins: [
|
|
149
|
+
// Plugin to remove leading comments attached to TypeScript-only constructs
|
|
150
|
+
{
|
|
151
|
+
name: "detype-comment-remover",
|
|
152
|
+
visitor: {
|
|
153
|
+
TSTypeAliasDeclaration: removeComments,
|
|
154
|
+
TSInterfaceDeclaration: removeComments,
|
|
155
|
+
TSDeclareFunction: removeComments,
|
|
156
|
+
TSDeclareMethod: removeComments,
|
|
157
|
+
TSImportType: removeComments
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
].filter(Boolean),
|
|
161
|
+
presets: [import_preset_typescript.default],
|
|
162
|
+
generatorOpts: {
|
|
163
|
+
shouldPrintComment: (comment) => comment !== "@detype: remove-me" && (!options.removeTsComments || !comment.match(/^\s*(@ts-ignore|@ts-expect-error)/))
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
if (options.customizeBabelConfig) {
|
|
167
|
+
options.customizeBabelConfig(babelConfig);
|
|
168
|
+
}
|
|
169
|
+
const babelOutput = await (0, import_core.transformAsync)(code, babelConfig);
|
|
170
|
+
if (!babelOutput || babelOutput.code === void 0 || babelOutput.code === null) {
|
|
171
|
+
throw new Error("Babel error");
|
|
172
|
+
}
|
|
173
|
+
return babelOutput.code.replaceAll(/\n\n*/g, "\n").replace(
|
|
174
|
+
/\/\* @detype: empty-line=([0-9]+) \*\//g,
|
|
175
|
+
(_match, p1) => `
|
|
176
|
+
`.repeat(p1 - 2)
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
async function removeTypesFromVueSfcScript(code, fileName, script, templateAst, options) {
|
|
180
|
+
if (script === null || script.lang !== "ts") return code;
|
|
181
|
+
if (script.setup && templateAst) {
|
|
182
|
+
const expressions = /* @__PURE__ */ new Set();
|
|
183
|
+
(0, import_template_ast_types.traverse)(templateAst, {
|
|
184
|
+
enter(node) {
|
|
185
|
+
if ((0, import_template_ast_types.isSimpleExpressionNode)(node) && !node.isStatic) {
|
|
186
|
+
expressions.add(`[${node.content}]`);
|
|
187
|
+
} else if ((0, import_template_ast_types.isComponentNode)(node)) {
|
|
188
|
+
expressions.add(`[${node.tag}]`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
script.content += "/* @detype: remove-after-this */" + [...expressions].join(";");
|
|
193
|
+
}
|
|
194
|
+
let scriptCode = await removeTypes(script.content, fileName + ".ts", options);
|
|
195
|
+
const removeAfterIndex = scriptCode.indexOf(
|
|
196
|
+
"/* @detype: remove-after-this */"
|
|
197
|
+
);
|
|
198
|
+
if (removeAfterIndex >= 0) {
|
|
199
|
+
scriptCode = scriptCode.slice(0, removeAfterIndex);
|
|
200
|
+
}
|
|
201
|
+
let before = code.slice(0, script.loc.start.offset);
|
|
202
|
+
const after = code.slice(script.loc.end.offset);
|
|
203
|
+
const matches = before.match(/\blang\s*=\s*["']ts["']/);
|
|
204
|
+
if (matches) {
|
|
205
|
+
const lastMatch = matches[matches.length - 1];
|
|
206
|
+
const lastMatchIndex = before.lastIndexOf(lastMatch);
|
|
207
|
+
before = before.slice(0, lastMatchIndex) + before.slice(lastMatchIndex + lastMatch.length);
|
|
208
|
+
}
|
|
209
|
+
return before + scriptCode + after;
|
|
210
|
+
}
|
|
211
|
+
function processMagicComments(input) {
|
|
212
|
+
const REPLACE_COMMENT = "// @detype: replace\n";
|
|
213
|
+
const WITH_COMMENT = "// @detype: with\n";
|
|
214
|
+
const END_COMMENT = "// @detype: end\n";
|
|
215
|
+
let start = input.indexOf(REPLACE_COMMENT);
|
|
216
|
+
while (start >= 0) {
|
|
217
|
+
const middle = input.indexOf(WITH_COMMENT, start);
|
|
218
|
+
if (middle < 0) return input;
|
|
219
|
+
const middleEnd = middle + WITH_COMMENT.length;
|
|
220
|
+
const end = input.indexOf(END_COMMENT, middleEnd);
|
|
221
|
+
if (end < 0) return input;
|
|
222
|
+
const endEnd = end + END_COMMENT.length;
|
|
223
|
+
const before = input.slice(0, start);
|
|
224
|
+
const newText = input.slice(middleEnd, end).replaceAll(/^\s*\/\//gm, "");
|
|
225
|
+
const after = input.slice(endEnd);
|
|
226
|
+
input = before + newText + after;
|
|
227
|
+
start = input.indexOf(REPLACE_COMMENT, before.length + newText.length);
|
|
228
|
+
}
|
|
229
|
+
return input;
|
|
230
|
+
}
|
|
231
|
+
async function removeMagicComments(code, fileName, prettierOptions) {
|
|
232
|
+
const REPLACE_COMMENT = "// @detype: replace\n";
|
|
233
|
+
const WITH_COMMENT = "// @detype: with\n";
|
|
234
|
+
const END_COMMENT = "// @detype: end\n";
|
|
235
|
+
let start = code.indexOf(REPLACE_COMMENT);
|
|
236
|
+
let startEnd = start + REPLACE_COMMENT.length;
|
|
237
|
+
while (start >= 0) {
|
|
238
|
+
const middle = code.indexOf(WITH_COMMENT, start);
|
|
239
|
+
if (middle < 0) return code;
|
|
240
|
+
const middleEnd = middle + WITH_COMMENT.length;
|
|
241
|
+
const end = code.indexOf(END_COMMENT, middleEnd);
|
|
242
|
+
if (end < 0) return code;
|
|
243
|
+
const endEnd = end + END_COMMENT.length;
|
|
244
|
+
const before = code.slice(0, start);
|
|
245
|
+
const keptText = code.slice(startEnd, middle);
|
|
246
|
+
const after = code.slice(endEnd);
|
|
247
|
+
code = before + keptText + after;
|
|
248
|
+
start = code.indexOf(REPLACE_COMMENT, before.length + keptText.length);
|
|
249
|
+
startEnd = start + REPLACE_COMMENT.length;
|
|
250
|
+
}
|
|
251
|
+
code = await (0, import_prettier.format)(code, {
|
|
252
|
+
...prettierOptions,
|
|
253
|
+
filepath: fileName
|
|
254
|
+
});
|
|
255
|
+
return code;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// src/transformFile.ts
|
|
259
|
+
var import_node_fs = __toESM(require("fs"), 1);
|
|
260
|
+
var import_prettier2 = require("prettier");
|
|
261
|
+
var { readFile, writeFile } = import_node_fs.default.promises;
|
|
262
|
+
async function transformFile(inputFileName, outputFileName, options = {}) {
|
|
263
|
+
const code = await readFile(inputFileName, "utf-8");
|
|
264
|
+
const prettierOptions = await (0, import_prettier2.resolveConfig)(inputFileName);
|
|
265
|
+
const output = await transform(code, inputFileName, {
|
|
266
|
+
prettierOptions,
|
|
267
|
+
...options
|
|
268
|
+
});
|
|
269
|
+
await writeFile(outputFileName, output, "utf-8");
|
|
270
|
+
}
|
|
271
|
+
async function removeMagicCommentsFromFile(inputFileName, outputFileName) {
|
|
272
|
+
const code = await readFile(inputFileName, "utf-8");
|
|
273
|
+
const prettierConfig = await (0, import_prettier2.resolveConfig)(inputFileName);
|
|
274
|
+
const output = await removeMagicComments(code, inputFileName, prettierConfig);
|
|
275
|
+
await writeFile(outputFileName, output, "utf-8");
|
|
276
|
+
}
|
|
277
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
278
|
+
0 && (module.exports = {
|
|
279
|
+
removeMagicComments,
|
|
280
|
+
removeMagicCommentsFromFile,
|
|
281
|
+
transform,
|
|
282
|
+
transformFile
|
|
283
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Options } from 'prettier';
|
|
2
|
+
export { Options as PrettierOptions } from 'prettier';
|
|
3
|
+
import { TransformOptions as TransformOptions$1 } from '@babel/core';
|
|
4
|
+
|
|
5
|
+
interface RemoveTypeOptions {
|
|
6
|
+
/** Whether to remove ts-ignore and ts-expect-error comments */
|
|
7
|
+
removeTsComments?: boolean;
|
|
8
|
+
/** Escape hatch for customizing Babel configuration */
|
|
9
|
+
customizeBabelConfig?(config: TransformOptions$1): void;
|
|
10
|
+
}
|
|
11
|
+
interface TransformOptions extends RemoveTypeOptions {
|
|
12
|
+
/** Prettier options */
|
|
13
|
+
prettierOptions?: Options | null;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Transform TypeScript code into vanilla JavaScript without affecting the formatting
|
|
17
|
+
* @param code Source coude
|
|
18
|
+
* @param fileName File name for the source
|
|
19
|
+
* @param options Options
|
|
20
|
+
*/
|
|
21
|
+
declare function transform(code: string, fileName: string, options?: TransformOptions): Promise<string>;
|
|
22
|
+
/**
|
|
23
|
+
* Removes magic comments without performing the TS to JS transform
|
|
24
|
+
* @param code Source coude
|
|
25
|
+
* @param fileName File name for the source
|
|
26
|
+
* @param prettierOptions Options to pass to prettier
|
|
27
|
+
*/
|
|
28
|
+
declare function removeMagicComments(code: string, fileName: string, prettierOptions?: Options | null): Promise<string>;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Transform the input file and write the output to another file
|
|
32
|
+
* @param inputFileName
|
|
33
|
+
* @param outputFileName
|
|
34
|
+
*/
|
|
35
|
+
declare function transformFile(inputFileName: string, outputFileName: string, options?: RemoveTypeOptions): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Remove magic comments from the input file and write the output to another file
|
|
38
|
+
* @param inputFileName
|
|
39
|
+
* @param outputFileName
|
|
40
|
+
*/
|
|
41
|
+
declare function removeMagicCommentsFromFile(inputFileName: string, outputFileName: string): Promise<void>;
|
|
42
|
+
|
|
43
|
+
export { removeMagicComments, removeMagicCommentsFromFile, transform, transformFile };
|
package/dist/index.js
CHANGED
|
@@ -1,283 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
var __create = Object.create;
|
|
3
|
-
var __defProp = Object.defineProperty;
|
|
4
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __export = (target, all) => {
|
|
9
|
-
for (var name in all)
|
|
10
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
-
};
|
|
12
|
-
var __copyProps = (to, from, except, desc) => {
|
|
13
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
-
for (let key of __getOwnPropNames(from))
|
|
15
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
-
mod
|
|
27
|
-
));
|
|
28
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
-
|
|
30
|
-
// src/index.ts
|
|
31
|
-
var src_exports = {};
|
|
32
|
-
__export(src_exports, {
|
|
33
|
-
removeMagicComments: () => removeMagicComments,
|
|
34
|
-
removeMagicCommentsFromFile: () => removeMagicCommentsFromFile,
|
|
35
|
-
transform: () => transform,
|
|
36
|
-
transformFile: () => transformFile
|
|
37
|
-
});
|
|
38
|
-
module.exports = __toCommonJS(src_exports);
|
|
39
|
-
|
|
40
|
-
// src/transform.ts
|
|
41
|
-
var import_core = require("@babel/core");
|
|
42
|
-
var import_prettier = require("prettier");
|
|
43
|
-
var import_compiler_sfc = require("@vuedx/compiler-sfc");
|
|
44
|
-
var import_compiler_sfc2 = require("@vue/compiler-sfc");
|
|
45
|
-
var import_template_ast_types = require("@vuedx/template-ast-types");
|
|
46
|
-
var import_preset_typescript = __toESM(require("@babel/preset-typescript"));
|
|
47
|
-
var import_string_prototype = require("string.prototype.replaceall");
|
|
48
|
-
(0, import_string_prototype.shim)();
|
|
49
|
-
function getDefinePropsObject(content) {
|
|
50
|
-
const matched = /\sprops:\s*\{/m.exec(content);
|
|
51
|
-
if (matched) {
|
|
52
|
-
const startContentIndex = matched.index + matched[0].length - 1;
|
|
53
|
-
let leftBracketCount = 1;
|
|
54
|
-
let endContentIndex = startContentIndex + 1;
|
|
55
|
-
while (leftBracketCount) {
|
|
56
|
-
if (content.charAt(endContentIndex) === "{") {
|
|
57
|
-
leftBracketCount++;
|
|
58
|
-
} else if (content.charAt(endContentIndex) === "}") {
|
|
59
|
-
leftBracketCount--;
|
|
60
|
-
}
|
|
61
|
-
endContentIndex++;
|
|
62
|
-
}
|
|
63
|
-
return content.substring(startContentIndex, endContentIndex);
|
|
64
|
-
}
|
|
65
|
-
return "";
|
|
66
|
-
}
|
|
67
|
-
async function transform(code, fileName, options = {}) {
|
|
68
|
-
const { prettierOptions, ...removeTypeOptions } = options;
|
|
69
|
-
const originalCode = code;
|
|
70
|
-
const originalFileName = fileName;
|
|
71
|
-
let propsContent = "";
|
|
72
|
-
let emitsContent = "";
|
|
73
|
-
code = code.replaceAll("\r\n", "\n");
|
|
74
|
-
if (fileName.endsWith(".vue")) {
|
|
75
|
-
const parsedVue = (0, import_compiler_sfc.parse)(code);
|
|
76
|
-
if (parsedVue.descriptor.script?.lang !== "ts" && parsedVue.descriptor.scriptSetup?.lang !== "ts") {
|
|
77
|
-
return originalCode;
|
|
78
|
-
}
|
|
79
|
-
let { script: script1, scriptSetup: script2 } = parsedVue.descriptor;
|
|
80
|
-
const isContainsDefinePropsType = script2?.content.match(/defineProps\s*</m);
|
|
81
|
-
const isContainsDefineEmitType = script2?.content.match(/defineEmits\s*</m);
|
|
82
|
-
if (isContainsDefinePropsType || isContainsDefineEmitType) {
|
|
83
|
-
const { content } = (0, import_compiler_sfc2.compileScript)(parsedVue.descriptor, {
|
|
84
|
-
id: "xxxxxxx"
|
|
85
|
-
});
|
|
86
|
-
if (isContainsDefinePropsType) {
|
|
87
|
-
propsContent = getDefinePropsObject(content);
|
|
88
|
-
}
|
|
89
|
-
if (isContainsDefineEmitType) {
|
|
90
|
-
emitsContent = content.match(/\semits:\s(\[.*\]?)/m)?.[1] || "";
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
if (script1 && script2 && script1.loc.start.offset < script2.loc.start.offset) {
|
|
94
|
-
[script2, script1] = [script1, script2];
|
|
95
|
-
}
|
|
96
|
-
code = await removeTypesFromVueSfcScript(
|
|
97
|
-
code,
|
|
98
|
-
fileName,
|
|
99
|
-
script1,
|
|
100
|
-
parsedVue.descriptor.template?.ast,
|
|
101
|
-
removeTypeOptions
|
|
102
|
-
);
|
|
103
|
-
code = await removeTypesFromVueSfcScript(
|
|
104
|
-
code,
|
|
105
|
-
fileName,
|
|
106
|
-
script2,
|
|
107
|
-
parsedVue.descriptor.template?.ast,
|
|
108
|
-
removeTypeOptions
|
|
109
|
-
);
|
|
110
|
-
} else {
|
|
111
|
-
code = await removeTypes(code, fileName, removeTypeOptions);
|
|
112
|
-
}
|
|
113
|
-
if (propsContent) {
|
|
114
|
-
code = code.replace("defineProps(", (str) => `${str}${propsContent}`);
|
|
115
|
-
}
|
|
116
|
-
if (emitsContent) {
|
|
117
|
-
code = code.replace("defineEmits(", (str) => `${str}${emitsContent}`);
|
|
118
|
-
}
|
|
119
|
-
code = await (0, import_prettier.format)(code, {
|
|
120
|
-
...prettierOptions,
|
|
121
|
-
filepath: originalFileName
|
|
122
|
-
});
|
|
123
|
-
return code;
|
|
124
|
-
}
|
|
125
|
-
async function removeTypes(code, fileName, options) {
|
|
126
|
-
code = code.replace(
|
|
127
|
-
/\n\n+/g,
|
|
128
|
-
(match) => `
|
|
129
|
-
/* @detype: empty-line=${match.length} */
|
|
130
|
-
`
|
|
131
|
-
);
|
|
132
|
-
code = processMagicComments(code);
|
|
133
|
-
const removeComments = {
|
|
134
|
-
enter(p) {
|
|
135
|
-
if (!p.node.leadingComments) return;
|
|
136
|
-
for (let i = p.node.leadingComments.length - 1; i >= 0; i--) {
|
|
137
|
-
const comment = p.node.leadingComments[i];
|
|
138
|
-
if (code.slice(comment.end).match(/^\s*\n\s*\n/) || comment.value.includes("@detype: empty-line")) {
|
|
139
|
-
break;
|
|
140
|
-
}
|
|
141
|
-
comment.value = "@detype: remove-me";
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
};
|
|
145
|
-
const babelConfig = {
|
|
146
|
-
filename: fileName,
|
|
147
|
-
retainLines: true,
|
|
148
|
-
plugins: [
|
|
149
|
-
// Plugin to remove leading comments attached to TypeScript-only constructs
|
|
150
|
-
{
|
|
151
|
-
name: "detype-comment-remover",
|
|
152
|
-
visitor: {
|
|
153
|
-
TSTypeAliasDeclaration: removeComments,
|
|
154
|
-
TSInterfaceDeclaration: removeComments,
|
|
155
|
-
TSDeclareFunction: removeComments,
|
|
156
|
-
TSDeclareMethod: removeComments,
|
|
157
|
-
TSImportType: removeComments
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
].filter(Boolean),
|
|
161
|
-
presets: [import_preset_typescript.default],
|
|
162
|
-
generatorOpts: {
|
|
163
|
-
shouldPrintComment: (comment) => comment !== "@detype: remove-me" && (!options.removeTsComments || !comment.match(/^\s*(@ts-ignore|@ts-expect-error)/))
|
|
164
|
-
}
|
|
165
|
-
};
|
|
166
|
-
if (options.customizeBabelConfig) {
|
|
167
|
-
options.customizeBabelConfig(babelConfig);
|
|
168
|
-
}
|
|
169
|
-
const babelOutput = await (0, import_core.transformAsync)(code, babelConfig);
|
|
170
|
-
if (!babelOutput || babelOutput.code === void 0 || babelOutput.code === null) {
|
|
171
|
-
throw new Error("Babel error");
|
|
172
|
-
}
|
|
173
|
-
return babelOutput.code.replaceAll(/\n\n*/g, "\n").replace(
|
|
174
|
-
/\/\* @detype: empty-line=([0-9]+) \*\//g,
|
|
175
|
-
(_match, p1) => `
|
|
176
|
-
`.repeat(p1 - 2)
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
async function removeTypesFromVueSfcScript(code, fileName, script, templateAst, options) {
|
|
180
|
-
if (script === null || script.lang !== "ts") return code;
|
|
181
|
-
if (script.setup && templateAst) {
|
|
182
|
-
const expressions = /* @__PURE__ */ new Set();
|
|
183
|
-
(0, import_template_ast_types.traverse)(templateAst, {
|
|
184
|
-
enter(node) {
|
|
185
|
-
if ((0, import_template_ast_types.isSimpleExpressionNode)(node) && !node.isStatic) {
|
|
186
|
-
expressions.add(`[${node.content}]`);
|
|
187
|
-
} else if ((0, import_template_ast_types.isComponentNode)(node)) {
|
|
188
|
-
expressions.add(`[${node.tag}]`);
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
});
|
|
192
|
-
script.content += "/* @detype: remove-after-this */" + [...expressions].join(";");
|
|
193
|
-
}
|
|
194
|
-
let scriptCode = await removeTypes(script.content, fileName + ".ts", options);
|
|
195
|
-
const removeAfterIndex = scriptCode.indexOf(
|
|
196
|
-
"/* @detype: remove-after-this */"
|
|
197
|
-
);
|
|
198
|
-
if (removeAfterIndex >= 0) {
|
|
199
|
-
scriptCode = scriptCode.slice(0, removeAfterIndex);
|
|
200
|
-
}
|
|
201
|
-
let before = code.slice(0, script.loc.start.offset);
|
|
202
|
-
const after = code.slice(script.loc.end.offset);
|
|
203
|
-
const matches = before.match(/\blang\s*=\s*["']ts["']/);
|
|
204
|
-
if (matches) {
|
|
205
|
-
const lastMatch = matches[matches.length - 1];
|
|
206
|
-
const lastMatchIndex = before.lastIndexOf(lastMatch);
|
|
207
|
-
before = before.slice(0, lastMatchIndex) + before.slice(lastMatchIndex + lastMatch.length);
|
|
208
|
-
}
|
|
209
|
-
return before + scriptCode + after;
|
|
210
|
-
}
|
|
211
|
-
function processMagicComments(input) {
|
|
212
|
-
const REPLACE_COMMENT = "// @detype: replace\n";
|
|
213
|
-
const WITH_COMMENT = "// @detype: with\n";
|
|
214
|
-
const END_COMMENT = "// @detype: end\n";
|
|
215
|
-
let start = input.indexOf(REPLACE_COMMENT);
|
|
216
|
-
while (start >= 0) {
|
|
217
|
-
const middle = input.indexOf(WITH_COMMENT, start);
|
|
218
|
-
if (middle < 0) return input;
|
|
219
|
-
const middleEnd = middle + WITH_COMMENT.length;
|
|
220
|
-
const end = input.indexOf(END_COMMENT, middleEnd);
|
|
221
|
-
if (end < 0) return input;
|
|
222
|
-
const endEnd = end + END_COMMENT.length;
|
|
223
|
-
const before = input.slice(0, start);
|
|
224
|
-
const newText = input.slice(middleEnd, end).replaceAll(/^\s*\/\//gm, "");
|
|
225
|
-
const after = input.slice(endEnd);
|
|
226
|
-
input = before + newText + after;
|
|
227
|
-
start = input.indexOf(REPLACE_COMMENT, before.length + newText.length);
|
|
228
|
-
}
|
|
229
|
-
return input;
|
|
230
|
-
}
|
|
231
|
-
async function removeMagicComments(code, fileName, prettierOptions) {
|
|
232
|
-
const REPLACE_COMMENT = "// @detype: replace\n";
|
|
233
|
-
const WITH_COMMENT = "// @detype: with\n";
|
|
234
|
-
const END_COMMENT = "// @detype: end\n";
|
|
235
|
-
let start = code.indexOf(REPLACE_COMMENT);
|
|
236
|
-
let startEnd = start + REPLACE_COMMENT.length;
|
|
237
|
-
while (start >= 0) {
|
|
238
|
-
const middle = code.indexOf(WITH_COMMENT, start);
|
|
239
|
-
if (middle < 0) return code;
|
|
240
|
-
const middleEnd = middle + WITH_COMMENT.length;
|
|
241
|
-
const end = code.indexOf(END_COMMENT, middleEnd);
|
|
242
|
-
if (end < 0) return code;
|
|
243
|
-
const endEnd = end + END_COMMENT.length;
|
|
244
|
-
const before = code.slice(0, start);
|
|
245
|
-
const keptText = code.slice(startEnd, middle);
|
|
246
|
-
const after = code.slice(endEnd);
|
|
247
|
-
code = before + keptText + after;
|
|
248
|
-
start = code.indexOf(REPLACE_COMMENT, before.length + keptText.length);
|
|
249
|
-
startEnd = start + REPLACE_COMMENT.length;
|
|
250
|
-
}
|
|
251
|
-
code = await (0, import_prettier.format)(code, {
|
|
252
|
-
...prettierOptions,
|
|
253
|
-
filepath: fileName
|
|
254
|
-
});
|
|
255
|
-
return code;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// src/transformFile.ts
|
|
259
|
-
var import_node_fs = __toESM(require("fs"));
|
|
260
|
-
var import_prettier2 = require("prettier");
|
|
261
|
-
var { readFile, writeFile } = import_node_fs.default.promises;
|
|
262
|
-
async function transformFile(inputFileName, outputFileName, options = {}) {
|
|
263
|
-
const code = await readFile(inputFileName, "utf-8");
|
|
264
|
-
const prettierOptions = await (0, import_prettier2.resolveConfig)(inputFileName);
|
|
265
|
-
const output = await transform(code, inputFileName, {
|
|
266
|
-
prettierOptions,
|
|
267
|
-
...options
|
|
268
|
-
});
|
|
269
|
-
await writeFile(outputFileName, output, "utf-8");
|
|
270
|
-
}
|
|
271
|
-
async function removeMagicCommentsFromFile(inputFileName, outputFileName) {
|
|
272
|
-
const code = await readFile(inputFileName, "utf-8");
|
|
273
|
-
const prettierConfig = await (0, import_prettier2.resolveConfig)(inputFileName);
|
|
274
|
-
const output = await removeMagicComments(code, inputFileName, prettierConfig);
|
|
275
|
-
await writeFile(outputFileName, output, "utf-8");
|
|
276
|
-
}
|
|
277
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
278
|
-
0 && (module.exports = {
|
|
1
|
+
import {
|
|
279
2
|
removeMagicComments,
|
|
280
3
|
removeMagicCommentsFromFile,
|
|
281
4
|
transform,
|
|
282
5
|
transformFile
|
|
283
|
-
}
|
|
6
|
+
} from "./chunk-ESFGC2T7.js";
|
|
7
|
+
export {
|
|
8
|
+
removeMagicComments,
|
|
9
|
+
removeMagicCommentsFromFile,
|
|
10
|
+
transform,
|
|
11
|
+
transformFile
|
|
12
|
+
};
|
package/package.json
CHANGED
|
@@ -1,19 +1,31 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "detype",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Removes TypeScript type annotations but keeps the formatting",
|
|
5
|
-
"
|
|
5
|
+
"type": "module",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"import": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
},
|
|
12
|
+
"require": {
|
|
13
|
+
"types": "./dist/index.d.cts",
|
|
14
|
+
"default": "./dist/index.cjs"
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
},
|
|
6
18
|
"bin": "detype.js",
|
|
7
19
|
"files": [
|
|
8
20
|
"dist/**/*",
|
|
9
21
|
"index.d.ts"
|
|
10
22
|
],
|
|
11
23
|
"dependencies": {
|
|
12
|
-
"@babel/core": "^7.
|
|
13
|
-
"@babel/preset-typescript": "^7.
|
|
14
|
-
"@babel/traverse": "^7.25.
|
|
15
|
-
"@vue/compiler-dom": "^3.5.
|
|
16
|
-
"@vue/compiler-sfc": "^3.5.
|
|
24
|
+
"@babel/core": "^7.26.0",
|
|
25
|
+
"@babel/preset-typescript": "^7.26.0",
|
|
26
|
+
"@babel/traverse": "^7.25.9",
|
|
27
|
+
"@vue/compiler-dom": "^3.5.12",
|
|
28
|
+
"@vue/compiler-sfc": "^3.5.12",
|
|
17
29
|
"@vuedx/compiler-sfc": "0.7.1",
|
|
18
30
|
"@vuedx/template-ast-types": "0.7.1",
|
|
19
31
|
"fast-glob": "^3.3.2",
|
|
@@ -21,15 +33,15 @@
|
|
|
21
33
|
"string.prototype.replaceall": "^1.0.10"
|
|
22
34
|
},
|
|
23
35
|
"devDependencies": {
|
|
24
|
-
"@cyco130/eslint-config": "^
|
|
36
|
+
"@cyco130/eslint-config": "^5.0.0",
|
|
25
37
|
"@types/babel__core": "^7.20.5",
|
|
26
38
|
"@types/babel__traverse": "^7.20.6",
|
|
27
|
-
"@types/node": "22.
|
|
28
|
-
"eslint": "^
|
|
39
|
+
"@types/node": "22.8.6",
|
|
40
|
+
"eslint": "^9.14.0",
|
|
29
41
|
"rimraf": "^6.0.1",
|
|
30
|
-
"tsup": "^8.
|
|
31
|
-
"typescript": "^5.
|
|
32
|
-
"vitest": "2.
|
|
42
|
+
"tsup": "^8.3.5",
|
|
43
|
+
"typescript": "^5.6.3",
|
|
44
|
+
"vitest": "2.1.4"
|
|
33
45
|
},
|
|
34
46
|
"repository": {
|
|
35
47
|
"type": "git",
|