detype 0.3.5 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -26
- package/dist/cli.js +207 -61
- package/dist/index.js +43 -33
- package/index.d.ts +19 -3
- package/package.json +21 -20
package/README.md
CHANGED
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
npm i -g detype
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Suppose you have a library that you want to provide usage examples for. **detype** can help you generate vanilla JavaScript samples from TypeScript samples automatically and remove the burden of maintaining two separate versions of what is essentially the same code.
|
|
10
|
+
|
|
11
|
+
It is a command line tool and a library that removes type annotations and other TypeScript specific syntax constructs and outputs vanilla JavaScript **without altering the source formatting** too much. It supports `.ts`, `.tsx`, as well as `.vue` files.
|
|
10
12
|
|
|
11
13
|
In other words, it turns this:
|
|
12
14
|
|
|
@@ -43,26 +45,68 @@ export function bar(foo) {
|
|
|
43
45
|
}
|
|
44
46
|
```
|
|
45
47
|
|
|
46
|
-
|
|
48
|
+
The output is very close to hand-written JavaScript, especially if you were already using Prettier for formatting.
|
|
47
49
|
|
|
48
|
-
|
|
50
|
+
## Doesn't `tsc` already do that?
|
|
49
51
|
|
|
50
|
-
|
|
52
|
+
There are lots of tools for transpiling TypeScript into plain JavaScript (`tsc`, `babel`, `swc`, `esbuild`, `sucrase` etc.) but none of them is perfectly suitable for this specific use case. Most of them don't preserve the formatting at all. `sucrase` comes close, but it doesn't remove comments attached to TypeScript-only constructs.
|
|
51
53
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
+
`detype` uses [Babel](https://babeljs.io/), a small Babel plugin to remove comments attached to TypeScript-only constructs, and [Prettier](https://prettier.io/) under the hood. For Vue files, it also uses the tools from the [VueDX project](https://github.com/vuedx/languagetools).
|
|
55
|
+
|
|
56
|
+
## Magic comments
|
|
57
|
+
|
|
58
|
+
Sometimes you want the generated JavaScript to be slightly different than the TypeScript original. You can use the magic comments feature to achieve this:
|
|
59
|
+
|
|
60
|
+
Input:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
// @detype: replace
|
|
64
|
+
// These two lines will be removed
|
|
65
|
+
console.log("Hello from TypeScript");
|
|
66
|
+
// @detype: with
|
|
67
|
+
// // Notice the double comments!
|
|
68
|
+
// console.log("Hello from JavaScript");
|
|
69
|
+
// @detype: end
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Output:
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
// Notice the double comments!
|
|
76
|
+
console.log("Hello from JavaScript");
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
If you just want to remove the magic comments, you can use the `-m` CLI flag or the `removeMagicComments` function to generate uncluttered TypeScript like this:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
// These two lines will be removed
|
|
83
|
+
console.log("Hello from TypeScript");
|
|
54
84
|
```
|
|
55
85
|
|
|
86
|
+
## System requirements
|
|
87
|
+
|
|
56
88
|
`detype` requires Node version 12.22.7 or later.
|
|
57
89
|
|
|
58
90
|
## CLI Usage
|
|
59
91
|
|
|
60
|
-
```
|
|
61
|
-
detype
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
92
|
+
```
|
|
93
|
+
detype [-m | --remove-magic-comments] <INPUT> [OUTPUT]
|
|
94
|
+
|
|
95
|
+
INPUT Input file or directory
|
|
96
|
+
|
|
97
|
+
OUTPUT Output file or directory
|
|
98
|
+
(optional if it can be inferred and it won't overwrite the source file)
|
|
99
|
+
|
|
100
|
+
-m, --remove-magic-comments
|
|
101
|
+
Remove magic comments only, don't perform ts > js transform
|
|
102
|
+
|
|
103
|
+
detype [-v | --version]
|
|
104
|
+
|
|
105
|
+
Print version and exit
|
|
106
|
+
|
|
107
|
+
detype [-h | --help]
|
|
108
|
+
|
|
109
|
+
Print this help and exit
|
|
66
110
|
```
|
|
67
111
|
|
|
68
112
|
## Node API
|
|
@@ -70,31 +114,51 @@ detype input-dir output-dir # Process recursively, rename .ts(x) as .js(x)
|
|
|
70
114
|
```ts
|
|
71
115
|
// Transform TypeScript code into vanilla JavaScript without affecting the formatting
|
|
72
116
|
function transform(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
117
|
+
// Source code
|
|
118
|
+
code: string,
|
|
119
|
+
// File name for the source
|
|
120
|
+
fileName: string,
|
|
121
|
+
// Options to pass to prettier
|
|
122
|
+
prettierOptions?: PrettierOptions | null,
|
|
79
123
|
): Promise<string>;
|
|
80
124
|
|
|
81
125
|
// Transform the input file and write the output to another file
|
|
82
126
|
function transformFile(
|
|
83
|
-
|
|
84
|
-
|
|
127
|
+
inputFileName: string,
|
|
128
|
+
outputFileName: string,
|
|
129
|
+
): Promise<void>;
|
|
130
|
+
|
|
131
|
+
// Remove magic comments without performing the TS to JS transform
|
|
132
|
+
export function removeMagicComments(
|
|
133
|
+
// Source code
|
|
134
|
+
code: string,
|
|
135
|
+
// File name for the source
|
|
136
|
+
fileName: string,
|
|
137
|
+
// Options to pass to prettier
|
|
138
|
+
prettierOptions?: PrettierOptions | null,
|
|
139
|
+
): string;
|
|
140
|
+
|
|
141
|
+
// Remove magic comments from the input file and write the output to another file
|
|
142
|
+
export function removeMagicCommentsFromFile(
|
|
143
|
+
inputFileName: string,
|
|
144
|
+
outputFileName: string,
|
|
85
145
|
): Promise<void>;
|
|
86
146
|
```
|
|
87
147
|
|
|
88
148
|
## Change log
|
|
89
|
-
|
|
90
|
-
-
|
|
91
|
-
-
|
|
149
|
+
### 0.4
|
|
150
|
+
- feature: CLI support for removing magic comments
|
|
151
|
+
- chore: Improve documentation
|
|
152
|
+
|
|
153
|
+
### 0.3
|
|
154
|
+
- feature: Magic comments
|
|
155
|
+
- feature: Expose type declarations
|
|
92
156
|
- fix: Better empty line handling
|
|
93
157
|
|
|
94
|
-
|
|
95
|
-
-
|
|
158
|
+
### 0.2
|
|
159
|
+
- feature: for Vue single file components
|
|
96
160
|
|
|
97
|
-
|
|
161
|
+
### 0.1
|
|
98
162
|
- Initial release
|
|
99
163
|
|
|
100
164
|
## Credits
|
package/dist/cli.js
CHANGED
|
@@ -21,33 +21,30 @@ var __spreadValues = (a, b) => {
|
|
|
21
21
|
return a;
|
|
22
22
|
};
|
|
23
23
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
24
|
-
var
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
__defProp(target, key, { get: () => module2[key], enumerable: !(desc = __getOwnPropDesc(module2, key)) || desc.enumerable });
|
|
24
|
+
var __copyProps = (to, from, except, desc) => {
|
|
25
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
26
|
+
for (let key of __getOwnPropNames(from))
|
|
27
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
28
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
30
29
|
}
|
|
31
|
-
return
|
|
32
|
-
};
|
|
33
|
-
var __toModule = (module2) => {
|
|
34
|
-
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2);
|
|
30
|
+
return to;
|
|
35
31
|
};
|
|
32
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
|
|
36
33
|
|
|
37
34
|
// src/cli-lib.ts
|
|
38
|
-
var import_fs2 =
|
|
39
|
-
var import_path =
|
|
35
|
+
var import_fs2 = __toESM(require("fs"));
|
|
36
|
+
var import_path = __toESM(require("path"));
|
|
40
37
|
|
|
41
38
|
// src/transformFile.ts
|
|
42
|
-
var import_fs =
|
|
39
|
+
var import_fs = __toESM(require("fs"));
|
|
43
40
|
|
|
44
41
|
// src/transform.ts
|
|
45
|
-
var import_core =
|
|
46
|
-
var import_prettier =
|
|
47
|
-
var import_compiler_sfc =
|
|
48
|
-
var import_template_ast_types =
|
|
49
|
-
var import_preset_typescript =
|
|
50
|
-
var import_string_prototype =
|
|
42
|
+
var import_core = require("@babel/core");
|
|
43
|
+
var import_prettier = require("prettier");
|
|
44
|
+
var import_compiler_sfc = require("@vuedx/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");
|
|
51
48
|
(0, import_string_prototype.shim)();
|
|
52
49
|
async function transform(code, fileName, prettierOptions) {
|
|
53
50
|
var _a, _b, _c, _d;
|
|
@@ -117,7 +114,7 @@ async function removeTypesFromVueSfcScript(code, fileName, script, templateAst)
|
|
|
117
114
|
if (script === null || script.lang !== "ts")
|
|
118
115
|
return code;
|
|
119
116
|
if (script.setup && templateAst) {
|
|
120
|
-
const expressions = new Set();
|
|
117
|
+
const expressions = /* @__PURE__ */ new Set();
|
|
121
118
|
(0, import_template_ast_types.traverse)(templateAst, {
|
|
122
119
|
enter(node) {
|
|
123
120
|
if ((0, import_template_ast_types.isSimpleExpressionNode)(node) && !node.isStatic) {
|
|
@@ -166,9 +163,36 @@ function processMagicComments(input) {
|
|
|
166
163
|
}
|
|
167
164
|
return input;
|
|
168
165
|
}
|
|
166
|
+
function removeMagicComments(code, fileName, prettierOptions) {
|
|
167
|
+
const REPLACE_COMMENT = "// @detype: replace\n";
|
|
168
|
+
const WITH_COMMENT = "// @detype: with\n";
|
|
169
|
+
const END_COMMENT = "// @detype: end\n";
|
|
170
|
+
let start = code.indexOf(REPLACE_COMMENT);
|
|
171
|
+
let startEnd = start + REPLACE_COMMENT.length;
|
|
172
|
+
while (start >= 0) {
|
|
173
|
+
const middle = code.indexOf(WITH_COMMENT, start);
|
|
174
|
+
if (middle < 0)
|
|
175
|
+
return code;
|
|
176
|
+
const middleEnd = middle + WITH_COMMENT.length;
|
|
177
|
+
const end = code.indexOf(END_COMMENT, middleEnd);
|
|
178
|
+
if (end < 0)
|
|
179
|
+
return code;
|
|
180
|
+
const endEnd = end + END_COMMENT.length;
|
|
181
|
+
const before = code.slice(0, start);
|
|
182
|
+
const keptText = code.slice(startEnd, middle);
|
|
183
|
+
const after = code.slice(endEnd);
|
|
184
|
+
code = before + keptText + after;
|
|
185
|
+
start = code.indexOf(REPLACE_COMMENT, before.length + keptText.length);
|
|
186
|
+
startEnd = start + REPLACE_COMMENT.length;
|
|
187
|
+
}
|
|
188
|
+
code = (0, import_prettier.format)(code, __spreadProps(__spreadValues({}, prettierOptions), {
|
|
189
|
+
filepath: fileName
|
|
190
|
+
}));
|
|
191
|
+
return code;
|
|
192
|
+
}
|
|
169
193
|
|
|
170
194
|
// src/transformFile.ts
|
|
171
|
-
var import_prettier2 =
|
|
195
|
+
var import_prettier2 = require("prettier");
|
|
172
196
|
var { readFile, writeFile } = import_fs.default.promises;
|
|
173
197
|
async function transformFile(inputFileName, outputFileName) {
|
|
174
198
|
const code = await readFile(inputFileName, "utf-8");
|
|
@@ -176,14 +200,119 @@ async function transformFile(inputFileName, outputFileName) {
|
|
|
176
200
|
const output = await transform(code, inputFileName, prettierConfig);
|
|
177
201
|
await writeFile(outputFileName, output, "utf-8");
|
|
178
202
|
}
|
|
203
|
+
async function removeMagicCommentsFromFile(inputFileName, outputFileName) {
|
|
204
|
+
const code = await readFile(inputFileName, "utf-8");
|
|
205
|
+
const prettierConfig = await (0, import_prettier2.resolveConfig)(inputFileName);
|
|
206
|
+
const output = await removeMagicComments(code, inputFileName, prettierConfig);
|
|
207
|
+
await writeFile(outputFileName, output, "utf-8");
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// src/cli-lib.ts
|
|
211
|
+
var import_fast_glob = __toESM(require("fast-glob"));
|
|
212
|
+
|
|
213
|
+
// package.json
|
|
214
|
+
var name = "detype";
|
|
215
|
+
var version = "0.4.2";
|
|
216
|
+
var description = "Removes TypeScript type annotations but keeps the formatting";
|
|
217
|
+
var main = "dist/index.js";
|
|
218
|
+
var bin = "detype.js";
|
|
219
|
+
var engines = {
|
|
220
|
+
node: ">=12.22.7"
|
|
221
|
+
};
|
|
222
|
+
var scripts = {
|
|
223
|
+
prepublishOnly: "pnpm build",
|
|
224
|
+
build: "node build.mjs",
|
|
225
|
+
watch: "node build.mjs --watch",
|
|
226
|
+
test: "run-p 'test:*'",
|
|
227
|
+
"test:unit": "jest",
|
|
228
|
+
"test:typecheck": "tsc -p tsconfig.json --noEmit",
|
|
229
|
+
"test:lint": "eslint src --max-warnings 0",
|
|
230
|
+
format: "prettier . --write"
|
|
231
|
+
};
|
|
232
|
+
var files = [
|
|
233
|
+
"dist/**/*",
|
|
234
|
+
"index.d.ts"
|
|
235
|
+
];
|
|
236
|
+
var dependencies = {
|
|
237
|
+
"@babel/core": "^7.17.8",
|
|
238
|
+
"@babel/preset-typescript": "^7.16.7",
|
|
239
|
+
"@vuedx/compiler-sfc": "^0.7.1",
|
|
240
|
+
"@vuedx/template-ast-types": "^0.7.2",
|
|
241
|
+
"fast-glob": "^3.2.11",
|
|
242
|
+
prettier: "^2.6.2",
|
|
243
|
+
"string.prototype.replaceall": "^1.0.6"
|
|
244
|
+
};
|
|
245
|
+
var devDependencies = {
|
|
246
|
+
"@babel/traverse": "^7.17.3",
|
|
247
|
+
"@types/jest": "^27.4.1",
|
|
248
|
+
"@types/node": "17.0.23",
|
|
249
|
+
"@typescript-eslint/eslint-plugin": "^5.17.0",
|
|
250
|
+
"@typescript-eslint/parser": "^5.17.0",
|
|
251
|
+
esbuild: "^0.14.31",
|
|
252
|
+
"esbuild-jest": "^0.5.0",
|
|
253
|
+
"esbuild-node-externals": "^1.4.1",
|
|
254
|
+
eslint: "^8.12.0",
|
|
255
|
+
"eslint-config-prettier": "^8.5.0",
|
|
256
|
+
"eslint-import-resolver-typescript": "^2.7.1",
|
|
257
|
+
"eslint-plugin-import": "^2.25.4",
|
|
258
|
+
"eslint-plugin-no-only-tests": "^2.6.0",
|
|
259
|
+
"eslint-plugin-only-warn": "^1.0.3",
|
|
260
|
+
"eslint-plugin-ssr-friendly": "^1.0.6",
|
|
261
|
+
jest: "^27.5.1",
|
|
262
|
+
"npm-run-all": "^4.1.5",
|
|
263
|
+
rimraf: "^3.0.2",
|
|
264
|
+
typescript: "^4.6.3"
|
|
265
|
+
};
|
|
266
|
+
var repository = {
|
|
267
|
+
type: "git",
|
|
268
|
+
url: "git+https://github.com/cyco130/detype.git"
|
|
269
|
+
};
|
|
270
|
+
var keywords = [
|
|
271
|
+
"typescript",
|
|
272
|
+
"formatting",
|
|
273
|
+
"vue",
|
|
274
|
+
"sfc"
|
|
275
|
+
];
|
|
276
|
+
var author = "Fatih Ayg\xFCn <cyco130@gmail.com>";
|
|
277
|
+
var license = "MIT";
|
|
278
|
+
var bugs = {
|
|
279
|
+
url: "https://github.com/cyco130/detype/issues"
|
|
280
|
+
};
|
|
281
|
+
var homepage = "https://github.com/cyco130/detype#readme";
|
|
282
|
+
var package_default = {
|
|
283
|
+
name,
|
|
284
|
+
version,
|
|
285
|
+
description,
|
|
286
|
+
main,
|
|
287
|
+
bin,
|
|
288
|
+
engines,
|
|
289
|
+
scripts,
|
|
290
|
+
files,
|
|
291
|
+
dependencies,
|
|
292
|
+
devDependencies,
|
|
293
|
+
repository,
|
|
294
|
+
keywords,
|
|
295
|
+
author,
|
|
296
|
+
license,
|
|
297
|
+
bugs,
|
|
298
|
+
homepage
|
|
299
|
+
};
|
|
179
300
|
|
|
180
301
|
// src/cli-lib.ts
|
|
181
|
-
var import_fast_glob = __toModule(require("fast-glob"));
|
|
182
302
|
var { stat, mkdir } = import_fs2.default.promises;
|
|
183
|
-
async function cli(
|
|
184
|
-
|
|
303
|
+
async function cli(...args2) {
|
|
304
|
+
let [flag, input, output] = args2;
|
|
305
|
+
if (!flag || flag === "-h" || flag === "--help") {
|
|
185
306
|
printUsage();
|
|
186
|
-
return
|
|
307
|
+
return !!flag;
|
|
308
|
+
}
|
|
309
|
+
if (flag === "-v" || flag === "--version") {
|
|
310
|
+
console.log(VERSION);
|
|
311
|
+
return true;
|
|
312
|
+
}
|
|
313
|
+
const removeMagic = flag === "-m" || flag === "--remove-magic-comments";
|
|
314
|
+
if (!removeMagic) {
|
|
315
|
+
[input, output] = args2;
|
|
187
316
|
}
|
|
188
317
|
const inputStat = await stat(input);
|
|
189
318
|
if (inputStat.isDirectory()) {
|
|
@@ -192,19 +321,19 @@ async function cli(input, output) {
|
|
|
192
321
|
printUsage();
|
|
193
322
|
return false;
|
|
194
323
|
}
|
|
195
|
-
const
|
|
196
|
-
const dirs = [...new Set(
|
|
197
|
-
await
|
|
324
|
+
const files2 = (await (0, import_fast_glob.default)(import_path.default.join(input, "**/*.{ts,tsx,vue}"))).filter((file) => !file.endsWith(".d.ts"));
|
|
325
|
+
const dirs = [...new Set(files2.map((file) => import_path.default.dirname(file)))].sort();
|
|
326
|
+
await mkdir(output, { recursive: true });
|
|
198
327
|
for (const dir of dirs) {
|
|
199
328
|
const outDir = import_path.default.join(output, import_path.default.relative(input, dir));
|
|
200
329
|
if (outDir === output)
|
|
201
330
|
continue;
|
|
202
|
-
await
|
|
331
|
+
await mkdir(outDir, { recursive: true });
|
|
203
332
|
}
|
|
204
|
-
for (const file of
|
|
333
|
+
for (const file of files2) {
|
|
205
334
|
const inputDir = import_path.default.dirname(import_path.default.relative(input, file));
|
|
206
335
|
const outputName = inferName(file, import_path.default.join(output, inputDir));
|
|
207
|
-
await transformFile(file, outputName);
|
|
336
|
+
removeMagic ? await removeMagicCommentsFromFile(file, outputName) : await transformFile(file, outputName);
|
|
208
337
|
}
|
|
209
338
|
return true;
|
|
210
339
|
}
|
|
@@ -219,46 +348,63 @@ async function cli(input, output) {
|
|
|
219
348
|
output = inferName(input, output);
|
|
220
349
|
}
|
|
221
350
|
} else {
|
|
351
|
+
if (removeMagic) {
|
|
352
|
+
console.error("Output file name is required when removing magic comments");
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
if (input.endsWith(".vue")) {
|
|
356
|
+
console.error("Output file name is required for .vue files");
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
222
359
|
output = inferName(input);
|
|
223
360
|
}
|
|
224
361
|
const outputDir = import_path.default.dirname(output);
|
|
225
362
|
if (outputDir) {
|
|
226
|
-
await
|
|
363
|
+
await mkdir(outputDir, { recursive: true });
|
|
227
364
|
}
|
|
228
|
-
await transformFile(input, output);
|
|
365
|
+
removeMagic ? await removeMagicCommentsFromFile(input, output) : await transformFile(input, output);
|
|
229
366
|
return true;
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
return output;
|
|
244
|
-
}
|
|
245
|
-
async function mkdirp(dir) {
|
|
246
|
-
await mkdir(dir, { recursive: true }).catch((error) => {
|
|
247
|
-
if (error && error.code == "EEXIST") {
|
|
248
|
-
return;
|
|
367
|
+
function inferName(input2, outputDir2) {
|
|
368
|
+
let output2;
|
|
369
|
+
const { dir, name: name2, ext } = import_path.default.parse(input2);
|
|
370
|
+
if (removeMagic) {
|
|
371
|
+
output2 = import_path.default.join(outputDir2 != null ? outputDir2 : dir, `${name2}${ext}`);
|
|
372
|
+
} else if (ext === ".ts") {
|
|
373
|
+
output2 = import_path.default.join(outputDir2 != null ? outputDir2 : dir, name2 + ".js");
|
|
374
|
+
} else if (ext === ".tsx") {
|
|
375
|
+
output2 = import_path.default.join(outputDir2 != null ? outputDir2 : dir, name2 + ".jsx");
|
|
376
|
+
} else if (ext === ".vue") {
|
|
377
|
+
output2 = import_path.default.join(outputDir2 != null ? outputDir2 : dir, name2 + ".vue");
|
|
378
|
+
} else {
|
|
379
|
+
throw new Error(`Unknwon file extension ${input2}`);
|
|
249
380
|
}
|
|
250
|
-
|
|
251
|
-
}
|
|
381
|
+
return output2;
|
|
382
|
+
}
|
|
252
383
|
}
|
|
253
384
|
function printUsage() {
|
|
254
|
-
console.error(
|
|
255
|
-
detype input.ts output.js
|
|
256
|
-
detype file.ts # Output to file.js
|
|
257
|
-
detype file.tsx # Output to file.jsx
|
|
258
|
-
detype file.ts output-dir # Output to output-dir/file.sjs
|
|
259
|
-
detype input-dir output-dir # Process recursively, rename .ts(x) as .js(x)`);
|
|
385
|
+
console.error(USAGE);
|
|
260
386
|
}
|
|
387
|
+
var USAGE = `Usage:
|
|
388
|
+
|
|
389
|
+
detype [-m | --remove-magic-comments] <INPUT> [OUTPUT]
|
|
390
|
+
|
|
391
|
+
INPUT Input file or directory
|
|
392
|
+
|
|
393
|
+
OUTPUT Output file or directory
|
|
394
|
+
(optional if it can be inferred and won't it overwrite the source file)
|
|
395
|
+
|
|
396
|
+
-m, --remove-magic-comments
|
|
397
|
+
Remove magic comments only, don't perform ts > js transform
|
|
398
|
+
|
|
399
|
+
detype [-v | --version]
|
|
400
|
+
|
|
401
|
+
Print version and exit
|
|
402
|
+
|
|
403
|
+
detype [-h | --help]
|
|
404
|
+
|
|
405
|
+
Print this help and exit`;
|
|
406
|
+
var VERSION = package_default.version;
|
|
261
407
|
|
|
262
408
|
// src/cli.ts
|
|
263
409
|
var args = process.argv.slice(2);
|
|
264
|
-
cli(args
|
|
410
|
+
cli(...args).then((success) => process.exit(success ? 0 : 1));
|
package/dist/index.js
CHANGED
|
@@ -21,38 +21,38 @@ var __spreadValues = (a, b) => {
|
|
|
21
21
|
return a;
|
|
22
22
|
};
|
|
23
23
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
24
|
-
var __markAsModule = (target) => __defProp(target, "__esModule", { value: true });
|
|
25
24
|
var __export = (target, all) => {
|
|
26
|
-
__markAsModule(target);
|
|
27
25
|
for (var name in all)
|
|
28
26
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
29
27
|
};
|
|
30
|
-
var
|
|
31
|
-
if (
|
|
32
|
-
for (let key of __getOwnPropNames(
|
|
33
|
-
if (!__hasOwnProp.call(
|
|
34
|
-
__defProp(
|
|
28
|
+
var __copyProps = (to, from, except, desc) => {
|
|
29
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
30
|
+
for (let key of __getOwnPropNames(from))
|
|
31
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
32
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
35
33
|
}
|
|
36
|
-
return
|
|
37
|
-
};
|
|
38
|
-
var __toModule = (module2) => {
|
|
39
|
-
return __reExport(__markAsModule(__defProp(module2 != null ? __create(__getProtoOf(module2)) : {}, "default", module2 && module2.__esModule && "default" in module2 ? { get: () => module2.default, enumerable: true } : { value: module2, enumerable: true })), module2);
|
|
34
|
+
return to;
|
|
40
35
|
};
|
|
36
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod));
|
|
37
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
41
38
|
|
|
42
39
|
// src/index.ts
|
|
43
|
-
|
|
40
|
+
var src_exports = {};
|
|
41
|
+
__export(src_exports, {
|
|
44
42
|
removeMagicComments: () => removeMagicComments,
|
|
43
|
+
removeMagicCommentsFromFile: () => removeMagicCommentsFromFile,
|
|
45
44
|
transform: () => transform,
|
|
46
45
|
transformFile: () => transformFile
|
|
47
46
|
});
|
|
47
|
+
module.exports = __toCommonJS(src_exports);
|
|
48
48
|
|
|
49
49
|
// src/transform.ts
|
|
50
|
-
var import_core =
|
|
51
|
-
var import_prettier =
|
|
52
|
-
var import_compiler_sfc =
|
|
53
|
-
var import_template_ast_types =
|
|
54
|
-
var import_preset_typescript =
|
|
55
|
-
var import_string_prototype =
|
|
50
|
+
var import_core = require("@babel/core");
|
|
51
|
+
var import_prettier = require("prettier");
|
|
52
|
+
var import_compiler_sfc = require("@vuedx/compiler-sfc");
|
|
53
|
+
var import_template_ast_types = require("@vuedx/template-ast-types");
|
|
54
|
+
var import_preset_typescript = __toESM(require("@babel/preset-typescript"));
|
|
55
|
+
var import_string_prototype = require("string.prototype.replaceall");
|
|
56
56
|
(0, import_string_prototype.shim)();
|
|
57
57
|
async function transform(code, fileName, prettierOptions) {
|
|
58
58
|
var _a, _b, _c, _d;
|
|
@@ -122,7 +122,7 @@ async function removeTypesFromVueSfcScript(code, fileName, script, templateAst)
|
|
|
122
122
|
if (script === null || script.lang !== "ts")
|
|
123
123
|
return code;
|
|
124
124
|
if (script.setup && templateAst) {
|
|
125
|
-
const expressions = new Set();
|
|
125
|
+
const expressions = /* @__PURE__ */ new Set();
|
|
126
126
|
(0, import_template_ast_types.traverse)(templateAst, {
|
|
127
127
|
enter(node) {
|
|
128
128
|
if ((0, import_template_ast_types.isSimpleExpressionNode)(node) && !node.isStatic) {
|
|
@@ -171,34 +171,37 @@ function processMagicComments(input) {
|
|
|
171
171
|
}
|
|
172
172
|
return input;
|
|
173
173
|
}
|
|
174
|
-
function removeMagicComments(
|
|
174
|
+
function removeMagicComments(code, fileName, prettierOptions) {
|
|
175
175
|
const REPLACE_COMMENT = "// @detype: replace\n";
|
|
176
176
|
const WITH_COMMENT = "// @detype: with\n";
|
|
177
177
|
const END_COMMENT = "// @detype: end\n";
|
|
178
|
-
let start =
|
|
178
|
+
let start = code.indexOf(REPLACE_COMMENT);
|
|
179
179
|
let startEnd = start + REPLACE_COMMENT.length;
|
|
180
180
|
while (start >= 0) {
|
|
181
|
-
const middle =
|
|
181
|
+
const middle = code.indexOf(WITH_COMMENT, start);
|
|
182
182
|
if (middle < 0)
|
|
183
|
-
return
|
|
183
|
+
return code;
|
|
184
184
|
const middleEnd = middle + WITH_COMMENT.length;
|
|
185
|
-
const end =
|
|
185
|
+
const end = code.indexOf(END_COMMENT, middleEnd);
|
|
186
186
|
if (end < 0)
|
|
187
|
-
return
|
|
187
|
+
return code;
|
|
188
188
|
const endEnd = end + END_COMMENT.length;
|
|
189
|
-
const before =
|
|
190
|
-
const keptText =
|
|
191
|
-
const after =
|
|
192
|
-
|
|
193
|
-
start =
|
|
189
|
+
const before = code.slice(0, start);
|
|
190
|
+
const keptText = code.slice(startEnd, middle);
|
|
191
|
+
const after = code.slice(endEnd);
|
|
192
|
+
code = before + keptText + after;
|
|
193
|
+
start = code.indexOf(REPLACE_COMMENT, before.length + keptText.length);
|
|
194
194
|
startEnd = start + REPLACE_COMMENT.length;
|
|
195
195
|
}
|
|
196
|
-
|
|
196
|
+
code = (0, import_prettier.format)(code, __spreadProps(__spreadValues({}, prettierOptions), {
|
|
197
|
+
filepath: fileName
|
|
198
|
+
}));
|
|
199
|
+
return code;
|
|
197
200
|
}
|
|
198
201
|
|
|
199
202
|
// src/transformFile.ts
|
|
200
|
-
var import_fs =
|
|
201
|
-
var import_prettier2 =
|
|
203
|
+
var import_fs = __toESM(require("fs"));
|
|
204
|
+
var import_prettier2 = require("prettier");
|
|
202
205
|
var { readFile, writeFile } = import_fs.default.promises;
|
|
203
206
|
async function transformFile(inputFileName, outputFileName) {
|
|
204
207
|
const code = await readFile(inputFileName, "utf-8");
|
|
@@ -206,9 +209,16 @@ async function transformFile(inputFileName, outputFileName) {
|
|
|
206
209
|
const output = await transform(code, inputFileName, prettierConfig);
|
|
207
210
|
await writeFile(outputFileName, output, "utf-8");
|
|
208
211
|
}
|
|
212
|
+
async function removeMagicCommentsFromFile(inputFileName, outputFileName) {
|
|
213
|
+
const code = await readFile(inputFileName, "utf-8");
|
|
214
|
+
const prettierConfig = await (0, import_prettier2.resolveConfig)(inputFileName);
|
|
215
|
+
const output = await removeMagicComments(code, inputFileName, prettierConfig);
|
|
216
|
+
await writeFile(outputFileName, output, "utf-8");
|
|
217
|
+
}
|
|
209
218
|
// Annotate the CommonJS export names for ESM import in node:
|
|
210
219
|
0 && (module.exports = {
|
|
211
220
|
removeMagicComments,
|
|
221
|
+
removeMagicCommentsFromFile,
|
|
212
222
|
transform,
|
|
213
223
|
transformFile
|
|
214
224
|
});
|
package/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { PrettierOptions };
|
|
|
5
5
|
/**
|
|
6
6
|
* Transform TypeScript code into vanilla JavaScript without affecting the formatting
|
|
7
7
|
* @param code Source coude
|
|
8
|
-
* @param fileName File name for the source
|
|
8
|
+
* @param fileName File name for the source
|
|
9
9
|
* @param prettierOptions Options to pass to prettier
|
|
10
10
|
*/
|
|
11
11
|
export function transform(
|
|
@@ -26,6 +26,22 @@ export function transformFile(
|
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Removes magic comments without performing the TS to JS transform
|
|
29
|
-
* @param
|
|
29
|
+
* @param code Source coude
|
|
30
|
+
* @param fileName File name for the source
|
|
31
|
+
* @param prettierOptions Options to pass to prettier
|
|
32
|
+
*/
|
|
33
|
+
export function removeMagicComments(
|
|
34
|
+
code: string,
|
|
35
|
+
fileName: string,
|
|
36
|
+
prettierOptions?: PrettierOptions | null,
|
|
37
|
+
): string;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Remove magic comments from the input file and write the output to another file
|
|
41
|
+
* @param inputFileName
|
|
42
|
+
* @param outputFileName
|
|
30
43
|
*/
|
|
31
|
-
export function
|
|
44
|
+
export function removeMagicCommentsFromFile(
|
|
45
|
+
inputFileName: string,
|
|
46
|
+
outputFileName: string,
|
|
47
|
+
): Promise<void>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "detype",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "Removes TypeScript type annotations but keeps the formatting",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": "detype.js",
|
|
@@ -12,34 +12,34 @@
|
|
|
12
12
|
"index.d.ts"
|
|
13
13
|
],
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@babel/core": "^7.
|
|
16
|
-
"@babel/preset-typescript": "^7.
|
|
15
|
+
"@babel/core": "^7.17.8",
|
|
16
|
+
"@babel/preset-typescript": "^7.16.7",
|
|
17
17
|
"@vuedx/compiler-sfc": "^0.7.1",
|
|
18
18
|
"@vuedx/template-ast-types": "^0.7.2",
|
|
19
|
-
"fast-glob": "^3.2.
|
|
20
|
-
"prettier": "^2.
|
|
19
|
+
"fast-glob": "^3.2.11",
|
|
20
|
+
"prettier": "^2.6.2",
|
|
21
21
|
"string.prototype.replaceall": "^1.0.6"
|
|
22
22
|
},
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@babel/traverse": "^7.
|
|
25
|
-
"@types/jest": "^27.
|
|
26
|
-
"@types/node": "
|
|
27
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
28
|
-
"@typescript-eslint/parser": "^
|
|
29
|
-
"esbuild": "^0.
|
|
24
|
+
"@babel/traverse": "^7.17.3",
|
|
25
|
+
"@types/jest": "^27.4.1",
|
|
26
|
+
"@types/node": "17.0.23",
|
|
27
|
+
"@typescript-eslint/eslint-plugin": "^5.17.0",
|
|
28
|
+
"@typescript-eslint/parser": "^5.17.0",
|
|
29
|
+
"esbuild": "^0.14.31",
|
|
30
30
|
"esbuild-jest": "^0.5.0",
|
|
31
|
-
"esbuild-node-externals": "^1.
|
|
32
|
-
"eslint": "^
|
|
33
|
-
"eslint-config-prettier": "^8.
|
|
34
|
-
"eslint-import-resolver-typescript": "^2.
|
|
35
|
-
"eslint-plugin-import": "^2.
|
|
31
|
+
"esbuild-node-externals": "^1.4.1",
|
|
32
|
+
"eslint": "^8.12.0",
|
|
33
|
+
"eslint-config-prettier": "^8.5.0",
|
|
34
|
+
"eslint-import-resolver-typescript": "^2.7.1",
|
|
35
|
+
"eslint-plugin-import": "^2.25.4",
|
|
36
36
|
"eslint-plugin-no-only-tests": "^2.6.0",
|
|
37
37
|
"eslint-plugin-only-warn": "^1.0.3",
|
|
38
|
-
"eslint-plugin-ssr-friendly": "^1.0.
|
|
39
|
-
"jest": "^27.
|
|
38
|
+
"eslint-plugin-ssr-friendly": "^1.0.6",
|
|
39
|
+
"jest": "^27.5.1",
|
|
40
40
|
"npm-run-all": "^4.1.5",
|
|
41
41
|
"rimraf": "^3.0.2",
|
|
42
|
-
"typescript": "^4.
|
|
42
|
+
"typescript": "^4.6.3"
|
|
43
43
|
},
|
|
44
44
|
"repository": {
|
|
45
45
|
"type": "git",
|
|
@@ -65,5 +65,6 @@
|
|
|
65
65
|
"test:typecheck": "tsc -p tsconfig.json --noEmit",
|
|
66
66
|
"test:lint": "eslint src --max-warnings 0",
|
|
67
67
|
"format": "prettier . --write"
|
|
68
|
-
}
|
|
68
|
+
},
|
|
69
|
+
"readme": "# detype\n\n> Remove the types, keep the formatting\n\n```sh\nnpm i -g detype\n```\n\nSuppose you have a library that you want to provide usage examples for. **detype** can help you generate vanilla JavaScript samples from TypeScript samples automatically and remove the burden of maintaining two separate versions of what is essentially the same code.\n\nIt is a command line tool and a library that removes type annotations and other TypeScript specific syntax constructs and outputs vanilla JavaScript **without altering the source formatting** too much. It supports `.ts`, `.tsx`, as well as `.vue` files.\n\nIn other words, it turns this:\n\n```ts\nimport type { ParsedPath } from \"path\";\n\nlet x: string;\n\n// This comment should be kept\n\n// This comment should be deleted\n// Ditto for this\ninterface Foo {\n // This should go too\n bar: number;\n}\n\n// This comment should also be kept\nexport function bar(foo: Foo): Date {\n return new Date();\n}\n```\n\ninto this:\n\n```js\nlet x;\n\n// This comment should be kept\n\n// This comment should also be kept\nexport function bar(foo) {\n return new Date();\n}\n```\n\nThe output is very close to hand-written JavaScript, especially if you were already using Prettier for formatting.\n\n## Doesn't `tsc` already do that?\n\nThere are lots of tools for transpiling TypeScript into plain JavaScript (`tsc`, `babel`, `swc`, `esbuild`, `sucrase` etc.) but none of them is perfectly suitable for this specific use case. Most of them don't preserve the formatting at all. `sucrase` comes close, but it doesn't remove comments attached to TypeScript-only constructs.\n\n`detype` uses [Babel](https://babeljs.io/), a small Babel plugin to remove comments attached to TypeScript-only constructs, and [Prettier](https://prettier.io/) under the hood. For Vue files, it also uses the tools from the [VueDX project](https://github.com/vuedx/languagetools).\n\n## Magic comments\n\nSometimes you want the generated JavaScript to be slightly different than the TypeScript original. You can use the magic comments feature to achieve this:\n\nInput:\n\n```ts\n// @detype: replace\n// These two lines will be removed\nconsole.log(\"Hello from TypeScript\");\n// @detype: with\n// // Notice the double comments!\n// console.log(\"Hello from JavaScript\");\n// @detype: end\n```\n\nOutput:\n\n```js\n// Notice the double comments!\nconsole.log(\"Hello from JavaScript\");\n```\n\nIf you just want to remove the magic comments, you can use the `-m` CLI flag or the `removeMagicComments` function to generate uncluttered TypeScript like this:\n\n```ts\n// These two lines will be removed\nconsole.log(\"Hello from TypeScript\");\n```\n\n## System requirements\n\n`detype` requires Node version 12.22.7 or later.\n\n## CLI Usage\n\n```\ndetype [-m | --remove-magic-comments] <INPUT> [OUTPUT]\n\n INPUT Input file or directory\n\n OUTPUT Output file or directory\n (optional if it can be inferred and it won't overwrite the source file)\n\n -m, --remove-magic-comments\n Remove magic comments only, don't perform ts > js transform\n\ndetype [-v | --version]\n\n Print version and exit\n\ndetype [-h | --help]\n\n Print this help and exit\n```\n\n## Node API\n\n```ts\n// Transform TypeScript code into vanilla JavaScript without affecting the formatting\nfunction transform(\n // Source code\n code: string,\n // File name for the source\n fileName: string,\n // Options to pass to prettier\n prettierOptions?: PrettierOptions | null,\n): Promise<string>;\n\n// Transform the input file and write the output to another file\nfunction transformFile(\n inputFileName: string,\n outputFileName: string,\n): Promise<void>;\n\n// Remove magic comments without performing the TS to JS transform\nexport function removeMagicComments(\n // Source code\n code: string,\n // File name for the source\n fileName: string,\n // Options to pass to prettier\n prettierOptions?: PrettierOptions | null,\n): string;\n\n// Remove magic comments from the input file and write the output to another file\nexport function removeMagicCommentsFromFile(\n inputFileName: string,\n outputFileName: string,\n): Promise<void>;\n```\n\n## Change log\n### 0.4\n- feature: CLI support for removing magic comments\n- chore: Improve documentation\n\n### 0.3\n- feature: Magic comments\n- feature: Expose type declarations\n- fix: Better empty line handling\n\n### 0.2\n- feature: for Vue single file components\n\n### 0.1\n- Initial release\n\n## Credits\nFatih Aygün, under MIT License"
|
|
69
70
|
}
|