detype 1.0.12 → 1.1.1

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 CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- require("./dist/cli");
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
- "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 __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
- var import_fast_glob = __toESM(require("fast-glob"));
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.12",
14
+ version: "1.1.1",
276
15
  description: "Removes TypeScript type annotations but keeps the formatting",
277
- main: "dist/index.js",
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,27 +44,27 @@ var package_default = {
293
44
  "index.d.ts"
294
45
  ],
295
46
  dependencies: {
296
- "@babel/core": "^7.25.2",
297
- "@babel/preset-typescript": "^7.24.7",
298
- "@babel/traverse": "^7.25.6",
299
- "@vue/compiler-dom": "^3.5.5",
300
- "@vue/compiler-sfc": "^3.5.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.13",
51
+ "@vue/compiler-sfc": "^3.5.13",
301
52
  "@vuedx/compiler-sfc": "0.7.1",
302
53
  "@vuedx/template-ast-types": "0.7.1",
303
54
  "fast-glob": "^3.3.2",
304
- prettier: "^3.3.3",
55
+ prettier: "^3.4.1",
305
56
  "string.prototype.replaceall": "^1.0.10"
306
57
  },
307
58
  devDependencies: {
308
- "@cyco130/eslint-config": "^3.9.6",
59
+ "@cyco130/eslint-config": "^5.0.1",
309
60
  "@types/babel__core": "^7.20.5",
310
61
  "@types/babel__traverse": "^7.20.6",
311
- "@types/node": "22.5.5",
312
- eslint: "^8.57.0",
62
+ "@types/node": "22.10.1",
63
+ eslint: "^9.16.0",
313
64
  rimraf: "^6.0.1",
314
- tsup: "^8.2.4",
315
- typescript: "^5.6.2",
316
- vitest: "2.1.1"
65
+ tsup: "^8.3.5",
66
+ typescript: "^5.7.2",
67
+ vitest: "2.1.6"
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 } = import_node_fs2.default.promises;
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 (0, import_fast_glob.default)(unixify(input + "/**/*.{ts,tsx,vue}"))).filter((file) => !file.endsWith(".d.ts"));
391
- const dirs = [...new Set(files.map((file) => import_node_path.default.dirname(file)))].sort();
392
- await mkdir(import_node_path.default.normalize(output), { recursive: true });
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 = import_node_path.default.join(output, import_node_path.default.relative(input, dir));
145
+ const outDir = path.join(output, path.relative(input, dir));
395
146
  if (outDir === output) continue;
396
- await mkdir(import_node_path.default.normalize(outDir), { recursive: true });
147
+ await mkdir(path.normalize(outDir), { recursive: true });
397
148
  }
398
149
  for (const file of files) {
399
- const inputDir = import_node_path.default.dirname(import_node_path.default.relative(input, file));
400
- const outputName = inferName(file, import_node_path.default.join(output, inputDir));
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
- import_node_path.default.normalize(file),
404
- import_node_path.default.normalize(outputName)
154
+ path.normalize(file),
155
+ path.normalize(outputName)
405
156
  );
406
157
  } else {
407
- await transformFile(import_node_path.default.normalize(file), import_node_path.default.normalize(outputName), {
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 = import_node_path.default.dirname(output);
188
+ const outputDir = path.dirname(output);
438
189
  if (outputDir) {
439
- await mkdir(import_node_path.default.normalize(outputDir), { recursive: true });
190
+ await mkdir(path.normalize(outputDir), { recursive: true });
440
191
  }
441
192
  if (removeMagic) {
442
193
  await removeMagicCommentsFromFile(
443
- import_node_path.default.normalize(input),
444
- import_node_path.default.normalize(output)
194
+ path.normalize(input),
195
+ path.normalize(output)
445
196
  );
446
197
  } else {
447
- await transformFile(import_node_path.default.normalize(input), import_node_path.default.normalize(output), {
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 } = import_node_path.default.parse(input2);
205
+ const { dir, name, ext } = path.parse(input2);
455
206
  if (removeMagic) {
456
- output2 = import_node_path.default.join(outputDir2 ?? dir, `${name}${ext}`);
207
+ output2 = path.join(outputDir2 ?? dir, `${name}${ext}`);
457
208
  } else if (ext === ".ts") {
458
- output2 = import_node_path.default.join(outputDir2 ?? dir, name + ".js");
209
+ output2 = path.join(outputDir2 ?? dir, name + ".js");
459
210
  } else if (ext === ".tsx") {
460
- output2 = import_node_path.default.join(outputDir2 ?? dir, name + ".jsx");
211
+ output2 = path.join(outputDir2 ?? dir, name + ".jsx");
461
212
  } else if (ext === ".vue") {
462
- output2 = import_node_path.default.join(outputDir2 ?? dir, name + ".vue");
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(import_node_path.default.sep, "/");
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
+ });
@@ -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
- "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"));
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,35 +1,47 @@
1
1
  {
2
2
  "name": "detype",
3
- "version": "1.0.12",
3
+ "version": "1.1.1",
4
4
  "description": "Removes TypeScript type annotations but keeps the formatting",
5
- "main": "dist/index.js",
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.25.2",
13
- "@babel/preset-typescript": "^7.24.7",
14
- "@babel/traverse": "^7.25.6",
15
- "@vue/compiler-dom": "^3.5.5",
16
- "@vue/compiler-sfc": "^3.5.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.13",
28
+ "@vue/compiler-sfc": "^3.5.13",
17
29
  "@vuedx/compiler-sfc": "0.7.1",
18
30
  "@vuedx/template-ast-types": "0.7.1",
19
31
  "fast-glob": "^3.3.2",
20
- "prettier": "^3.3.3",
32
+ "prettier": "^3.4.1",
21
33
  "string.prototype.replaceall": "^1.0.10"
22
34
  },
23
35
  "devDependencies": {
24
- "@cyco130/eslint-config": "^3.9.6",
36
+ "@cyco130/eslint-config": "^5.0.1",
25
37
  "@types/babel__core": "^7.20.5",
26
38
  "@types/babel__traverse": "^7.20.6",
27
- "@types/node": "22.5.5",
28
- "eslint": "^8.57.0",
39
+ "@types/node": "22.10.1",
40
+ "eslint": "^9.16.0",
29
41
  "rimraf": "^6.0.1",
30
- "tsup": "^8.2.4",
31
- "typescript": "^5.6.2",
32
- "vitest": "2.1.1"
42
+ "tsup": "^8.3.5",
43
+ "typescript": "^5.7.2",
44
+ "vitest": "2.1.6"
33
45
  },
34
46
  "repository": {
35
47
  "type": "git",