detype 0.4.2 → 0.6.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/README.md CHANGED
@@ -22,13 +22,13 @@ let x: string;
22
22
  // This comment should be deleted
23
23
  // Ditto for this
24
24
  interface Foo {
25
- // This should go too
26
- bar: number;
25
+ // This should go too
26
+ bar: number;
27
27
  }
28
28
 
29
29
  // This comment should also be kept
30
30
  export function bar(foo: Foo): Date {
31
- return new Date();
31
+ return new Date();
32
32
  }
33
33
  ```
34
34
 
@@ -41,7 +41,7 @@ let x;
41
41
 
42
42
  // This comment should also be kept
43
43
  export function bar(foo) {
44
- return new Date();
44
+ return new Date();
45
45
  }
46
46
  ```
47
47
 
@@ -85,28 +85,31 @@ console.log("Hello from TypeScript");
85
85
 
86
86
  ## System requirements
87
87
 
88
- `detype` requires Node version 12.22.7 or later.
88
+ `detype` requires Node version 14.19.3 or later.
89
89
 
90
90
  ## CLI Usage
91
91
 
92
92
  ```
93
- detype [-m | --remove-magic-comments] <INPUT> [OUTPUT]
93
+ detype [-m | --remove-magic-comments] <INPUT> [OUTPUT]
94
94
 
95
- INPUT Input file or directory
95
+ INPUT Input file or directory
96
96
 
97
- OUTPUT Output file or directory
98
- (optional if it can be inferred and it won't overwrite the source file)
97
+ OUTPUT Output file or directory
98
+ (optional if it can be inferred and it won't overwrite the source file)
99
99
 
100
- -m, --remove-magic-comments
101
- Remove magic comments only, don't perform ts > js transform
100
+ -t, --remove-ts-comments
101
+ Remove @ts-ignore and @ts-expect-error comments
102
102
 
103
- detype [-v | --version]
103
+ -m, --remove-magic-comments
104
+ Remove magic comments only, don't perform ts > js transform
104
105
 
105
- Print version and exit
106
+ detype [-v | --version]
106
107
 
107
- detype [-h | --help]
108
+ Print version and exit
108
109
 
109
- Print this help and exit
110
+ detype [-h | --help]
111
+
112
+ Print this help and exit
110
113
  ```
111
114
 
112
115
  ## Node API
@@ -114,52 +117,68 @@ detype [-h | --help]
114
117
  ```ts
115
118
  // Transform TypeScript code into vanilla JavaScript without affecting the formatting
116
119
  function transform(
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,
120
+ // Source code
121
+ code: string,
122
+ // File name for the source
123
+ fileName: string,
124
+ // Options to pass to prettier
125
+ prettierOptions?: PrettierOptions | null,
123
126
  ): Promise<string>;
124
127
 
125
128
  // Transform the input file and write the output to another file
126
129
  function transformFile(
127
- inputFileName: string,
128
- outputFileName: string,
130
+ inputFileName: string,
131
+ outputFileName: string,
129
132
  ): Promise<void>;
130
133
 
131
134
  // Remove magic comments without performing the TS to JS transform
132
135
  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,
136
+ // Source code
137
+ code: string,
138
+ // File name for the source
139
+ fileName: string,
140
+ // Options to pass to prettier
141
+ prettierOptions?: PrettierOptions | null,
139
142
  ): string;
140
143
 
141
144
  // Remove magic comments from the input file and write the output to another file
142
145
  export function removeMagicCommentsFromFile(
143
- inputFileName: string,
144
- outputFileName: string,
146
+ inputFileName: string,
147
+ outputFileName: string,
145
148
  ): Promise<void>;
146
149
  ```
147
150
 
148
151
  ## Change log
152
+
153
+ ### 0.6
154
+
155
+ - feature: Option to remove @ts-ignore and @ts-expect-error comments
156
+ - fix: Preserve newline runs (especially in template literals!)
157
+
158
+ ### 0.5
159
+
160
+ - BREAKING CHANGE: Drop support for Node 12
161
+ - chore: Set up CI workflows
162
+
149
163
  ### 0.4
164
+
150
165
  - feature: CLI support for removing magic comments
151
166
  - chore: Improve documentation
152
167
 
153
168
  ### 0.3
169
+
154
170
  - feature: Magic comments
155
171
  - feature: Expose type declarations
156
172
  - fix: Better empty line handling
157
173
 
158
174
  ### 0.2
175
+
159
176
  - feature: for Vue single file components
160
177
 
161
178
  ### 0.1
179
+
162
180
  - Initial release
163
181
 
164
182
  ## Credits
165
- Fatih Aygün, under MIT License
183
+
184
+ Fatih Aygün, under MIT License
package/dist/cli.js CHANGED
@@ -1,26 +1,10 @@
1
+ "use strict";
1
2
  var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
- var __defProps = Object.defineProperties;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
6
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getOwnPropSymbols = Object.getOwnPropertySymbols;
8
6
  var __getProtoOf = Object.getPrototypeOf;
9
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
10
- var __propIsEnum = Object.prototype.propertyIsEnumerable;
11
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
12
- var __spreadValues = (a, b) => {
13
- for (var prop in b || (b = {}))
14
- if (__hasOwnProp.call(b, prop))
15
- __defNormalProp(a, prop, b[prop]);
16
- if (__getOwnPropSymbols)
17
- for (var prop of __getOwnPropSymbols(b)) {
18
- if (__propIsEnum.call(b, prop))
19
- __defNormalProp(a, prop, b[prop]);
20
- }
21
- return a;
22
- };
23
- var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
24
8
  var __copyProps = (to, from, except, desc) => {
25
9
  if (from && typeof from === "object" || typeof from === "function") {
26
10
  for (let key of __getOwnPropNames(from))
@@ -29,7 +13,10 @@ var __copyProps = (to, from, except, desc) => {
29
13
  }
30
14
  return to;
31
15
  };
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));
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
18
+ mod
19
+ ));
33
20
 
34
21
  // src/cli-lib.ts
35
22
  var import_fs2 = __toESM(require("fs"));
@@ -46,10 +33,12 @@ var import_template_ast_types = require("@vuedx/template-ast-types");
46
33
  var import_preset_typescript = __toESM(require("@babel/preset-typescript"));
47
34
  var import_string_prototype = require("string.prototype.replaceall");
48
35
  (0, import_string_prototype.shim)();
49
- async function transform(code, fileName, prettierOptions) {
36
+ async function transform(code, fileName, options = {}) {
50
37
  var _a, _b, _c, _d;
38
+ const { prettierOptions, ...removeTypeOptions } = options;
51
39
  const originalCode = code;
52
40
  const originalFileName = fileName;
41
+ code = code.replaceAll("\r\n", "\n");
53
42
  if (fileName.endsWith(".vue")) {
54
43
  const parsedVue = (0, import_compiler_sfc.parse)(code, { filename: fileName });
55
44
  if (((_a = parsedVue.descriptor.script) == null ? void 0 : _a.lang) !== "ts" && ((_b = parsedVue.descriptor.scriptSetup) == null ? void 0 : _b.lang) !== "ts") {
@@ -59,18 +48,36 @@ async function transform(code, fileName, prettierOptions) {
59
48
  if (script1 && script2 && script1.loc.start.offset < script2.loc.start.offset) {
60
49
  [script2, script1] = [script1, script2];
61
50
  }
62
- code = await removeTypesFromVueSfcScript(code, fileName, script1, (_c = parsedVue.descriptor.template) == null ? void 0 : _c.ast);
63
- code = await removeTypesFromVueSfcScript(code, fileName, script2, (_d = parsedVue.descriptor.template) == null ? void 0 : _d.ast);
51
+ code = await removeTypesFromVueSfcScript(
52
+ code,
53
+ fileName,
54
+ script1,
55
+ (_c = parsedVue.descriptor.template) == null ? void 0 : _c.ast,
56
+ removeTypeOptions
57
+ );
58
+ code = await removeTypesFromVueSfcScript(
59
+ code,
60
+ fileName,
61
+ script2,
62
+ (_d = parsedVue.descriptor.template) == null ? void 0 : _d.ast,
63
+ removeTypeOptions
64
+ );
64
65
  } else {
65
- code = await removeTypes(code, fileName);
66
+ code = await removeTypes(code, fileName, removeTypeOptions);
66
67
  }
67
- code = (0, import_prettier.format)(code, __spreadProps(__spreadValues({}, prettierOptions), {
68
+ code = (0, import_prettier.format)(code, {
69
+ ...prettierOptions,
68
70
  filepath: originalFileName
69
- }));
71
+ });
70
72
  return code;
71
73
  }
72
- async function removeTypes(code, fileName) {
73
- code = code.replaceAll(/\n\n+/g, "\n/* @detype: empty-line */\n");
74
+ async function removeTypes(code, fileName, options) {
75
+ code = code.replace(
76
+ /\n\n+/g,
77
+ (match) => `
78
+ /* @detype: empty-line=${match.length} */
79
+ `
80
+ );
74
81
  code = processMagicComments(code);
75
82
  const removeComments = {
76
83
  enter(p) {
@@ -102,15 +109,19 @@ async function removeTypes(code, fileName) {
102
109
  ].filter(Boolean),
103
110
  presets: [import_preset_typescript.default],
104
111
  generatorOpts: {
105
- shouldPrintComment: (comment) => comment !== "@detype: remove-me"
112
+ shouldPrintComment: (comment) => comment !== "@detype: remove-me" && (!options.removeTsComments || !comment.match(/^\s*(@ts-ignore|@ts-expect-error)/))
106
113
  }
107
114
  });
108
115
  if (!babelOutput || babelOutput.code === void 0 || babelOutput.code === null) {
109
116
  throw new Error("Babel error");
110
117
  }
111
- return babelOutput.code.replaceAll(/\n\n*/g, "\n").replaceAll("/* @detype: empty-line */", "\n\n");
118
+ return babelOutput.code.replaceAll(/\n\n*/g, "\n").replace(
119
+ /\/\* @detype: empty-line=([0-9]+) \*\//g,
120
+ (_match, p1) => `
121
+ `.repeat(p1 - 2)
122
+ );
112
123
  }
113
- async function removeTypesFromVueSfcScript(code, fileName, script, templateAst) {
124
+ async function removeTypesFromVueSfcScript(code, fileName, script, templateAst, options) {
114
125
  if (script === null || script.lang !== "ts")
115
126
  return code;
116
127
  if (script.setup && templateAst) {
@@ -126,8 +137,10 @@ async function removeTypesFromVueSfcScript(code, fileName, script, templateAst)
126
137
  });
127
138
  script.content += "/* @detype: remove-after-this */" + [...expressions].join(";");
128
139
  }
129
- let scriptCode = await removeTypes(script.content, fileName + ".ts");
130
- const removeAfterIndex = scriptCode.indexOf("/* @detype: remove-after-this */");
140
+ let scriptCode = await removeTypes(script.content, fileName + ".ts", options);
141
+ const removeAfterIndex = scriptCode.indexOf(
142
+ "/* @detype: remove-after-this */"
143
+ );
131
144
  if (removeAfterIndex >= 0) {
132
145
  scriptCode = scriptCode.slice(0, removeAfterIndex);
133
146
  }
@@ -185,19 +198,23 @@ function removeMagicComments(code, fileName, prettierOptions) {
185
198
  start = code.indexOf(REPLACE_COMMENT, before.length + keptText.length);
186
199
  startEnd = start + REPLACE_COMMENT.length;
187
200
  }
188
- code = (0, import_prettier.format)(code, __spreadProps(__spreadValues({}, prettierOptions), {
201
+ code = (0, import_prettier.format)(code, {
202
+ ...prettierOptions,
189
203
  filepath: fileName
190
- }));
204
+ });
191
205
  return code;
192
206
  }
193
207
 
194
208
  // src/transformFile.ts
195
209
  var import_prettier2 = require("prettier");
196
210
  var { readFile, writeFile } = import_fs.default.promises;
197
- async function transformFile(inputFileName, outputFileName) {
211
+ async function transformFile(inputFileName, outputFileName, options = {}) {
198
212
  const code = await readFile(inputFileName, "utf-8");
199
- const prettierConfig = await (0, import_prettier2.resolveConfig)(inputFileName);
200
- const output = await transform(code, inputFileName, prettierConfig);
213
+ const prettierOptions = await (0, import_prettier2.resolveConfig)(inputFileName);
214
+ const output = await transform(code, inputFileName, {
215
+ prettierOptions,
216
+ ...options
217
+ });
201
218
  await writeFile(outputFileName, output, "utf-8");
202
219
  }
203
220
  async function removeMagicCommentsFromFile(inputFileName, outputFileName) {
@@ -211,106 +228,111 @@ async function removeMagicCommentsFromFile(inputFileName, outputFileName) {
211
228
  var import_fast_glob = __toESM(require("fast-glob"));
212
229
 
213
230
  // 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
231
  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
232
+ name: "detype",
233
+ version: "0.6.0",
234
+ description: "Removes TypeScript type annotations but keeps the formatting",
235
+ main: "dist/index.js",
236
+ bin: "detype.js",
237
+ engines: {
238
+ node: ">=14.19.3"
239
+ },
240
+ scripts: {
241
+ prepack: "rimraf dist && pnpm build",
242
+ build: "tsup",
243
+ dev: "tsup --watch",
244
+ cq: "pnpm run test:typecheck && pnpm run test:lint && pnpm run test:prettier",
245
+ test: "run-p 'test:*'",
246
+ "test:unit": "vitest run",
247
+ "test:typecheck": "tsc -p tsconfig.json --noEmit",
248
+ "test:lint": "eslint src --max-warnings 0",
249
+ "test:prettier": "prettier --check --ignore-path .gitignore --ignore-unknown . '!pnpm-lock.yaml'",
250
+ format: "prettier . --write --ignore-path .gitignore --ignore-unknown . '!pnpm-lock.yaml'"
251
+ },
252
+ files: [
253
+ "dist/**/*",
254
+ "index.d.ts"
255
+ ],
256
+ dependencies: {
257
+ "@babel/core": "^7.18.9",
258
+ "@babel/preset-typescript": "^7.18.6",
259
+ "@vuedx/compiler-sfc": "^0.7.1",
260
+ "@vuedx/template-ast-types": "^0.7.3",
261
+ "fast-glob": "^3.2.11",
262
+ prettier: "^2.7.1",
263
+ "string.prototype.replaceall": "^1.0.6"
264
+ },
265
+ devDependencies: {
266
+ "@babel/traverse": "^7.18.9",
267
+ "@cyco130/eslint-config": "^2.1.1",
268
+ "@types/babel__core": "^7.1.19",
269
+ "@types/babel__traverse": "^7.17.1",
270
+ "@types/node": "18.6.2",
271
+ "@types/prettier": "^2.6.4",
272
+ eslint: "^8.20.0",
273
+ "npm-run-all": "^4.1.5",
274
+ rimraf: "^3.0.2",
275
+ tsup: "^6.2.1",
276
+ typescript: "^4.7.4",
277
+ vitest: "^0.19.1"
278
+ },
279
+ repository: {
280
+ type: "git",
281
+ url: "git+https://github.com/cyco130/detype.git"
282
+ },
283
+ keywords: [
284
+ "typescript",
285
+ "formatting",
286
+ "vue",
287
+ "sfc"
288
+ ],
289
+ author: "Fatih Ayg\xFCn <cyco130@gmail.com>",
290
+ license: "MIT",
291
+ bugs: {
292
+ url: "https://github.com/cyco130/detype/issues"
293
+ },
294
+ homepage: "https://github.com/cyco130/detype#readme"
299
295
  };
300
296
 
301
297
  // src/cli-lib.ts
302
298
  var { stat, mkdir } = import_fs2.default.promises;
303
299
  async function cli(...args2) {
304
- let [flag, input, output] = args2;
305
- if (!flag || flag === "-h" || flag === "--help") {
300
+ let dashDash = false;
301
+ const params = [];
302
+ const flags = [];
303
+ for (const arg of args2) {
304
+ if (arg === "--") {
305
+ dashDash = true;
306
+ } else if (dashDash || !arg.startsWith("-")) {
307
+ params.push(arg);
308
+ } else {
309
+ flags.push(arg);
310
+ }
311
+ }
312
+ if (params.length > 2) {
313
+ console.error("Too many arguments");
314
+ return false;
315
+ }
316
+ let [input, output] = params;
317
+ if (params.length === 0 || flags.some((flag) => flag === "-h" || flag === "--help")) {
306
318
  printUsage();
307
- return !!flag;
319
+ return params.length > 0;
308
320
  }
309
- if (flag === "-v" || flag === "--version") {
321
+ if (flags.some((flag) => flag === "-v" || flag === "--version")) {
310
322
  console.log(VERSION);
311
323
  return true;
312
324
  }
313
- const removeMagic = flag === "-m" || flag === "--remove-magic-comments";
325
+ const removeMagic = flags.some(
326
+ (flag) => flag === "-m" || flag === "--remove-magic-comments"
327
+ );
328
+ const removeTsComments = flags.some(
329
+ (flag) => flag === "-t" || flag === "--remove-ts-comments"
330
+ );
331
+ if (removeMagic && removeTsComments) {
332
+ console.warn(
333
+ "--remove-ts-comments has no effect when --remove-magic-comments is used"
334
+ );
335
+ }
314
336
  if (!removeMagic) {
315
337
  [input, output] = args2;
316
338
  }
@@ -321,19 +343,28 @@ async function cli(...args2) {
321
343
  printUsage();
322
344
  return false;
323
345
  }
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 });
346
+ const files = (await (0, import_fast_glob.default)(unixify(input + "/**/*.{ts,tsx,vue}"))).filter(
347
+ (file) => !file.endsWith(".d.ts")
348
+ );
349
+ const dirs = [...new Set(files.map((file) => import_path.default.dirname(file)))].sort();
350
+ await mkdir(import_path.default.normalize(output), { recursive: true });
327
351
  for (const dir of dirs) {
328
352
  const outDir = import_path.default.join(output, import_path.default.relative(input, dir));
329
353
  if (outDir === output)
330
354
  continue;
331
- await mkdir(outDir, { recursive: true });
355
+ await mkdir(import_path.default.normalize(outDir), { recursive: true });
332
356
  }
333
- for (const file of files2) {
357
+ for (const file of files) {
334
358
  const inputDir = import_path.default.dirname(import_path.default.relative(input, file));
335
359
  const outputName = inferName(file, import_path.default.join(output, inputDir));
336
- removeMagic ? await removeMagicCommentsFromFile(file, outputName) : await transformFile(file, outputName);
360
+ removeMagic ? await removeMagicCommentsFromFile(
361
+ import_path.default.normalize(file),
362
+ import_path.default.normalize(outputName)
363
+ ) : await transformFile(
364
+ import_path.default.normalize(file),
365
+ import_path.default.normalize(outputName),
366
+ { removeTsComments }
367
+ );
337
368
  }
338
369
  return true;
339
370
  }
@@ -349,7 +380,9 @@ async function cli(...args2) {
349
380
  }
350
381
  } else {
351
382
  if (removeMagic) {
352
- console.error("Output file name is required when removing magic comments");
383
+ console.error(
384
+ "Output file name is required when removing magic comments"
385
+ );
353
386
  return false;
354
387
  }
355
388
  if (input.endsWith(".vue")) {
@@ -360,21 +393,26 @@ async function cli(...args2) {
360
393
  }
361
394
  const outputDir = import_path.default.dirname(output);
362
395
  if (outputDir) {
363
- await mkdir(outputDir, { recursive: true });
396
+ await mkdir(import_path.default.normalize(outputDir), { recursive: true });
364
397
  }
365
- removeMagic ? await removeMagicCommentsFromFile(input, output) : await transformFile(input, output);
398
+ removeMagic ? await removeMagicCommentsFromFile(
399
+ import_path.default.normalize(input),
400
+ import_path.default.normalize(output)
401
+ ) : await transformFile(import_path.default.normalize(input), import_path.default.normalize(output), {
402
+ removeTsComments
403
+ });
366
404
  return true;
367
405
  function inferName(input2, outputDir2) {
368
406
  let output2;
369
- const { dir, name: name2, ext } = import_path.default.parse(input2);
407
+ const { dir, name, ext } = import_path.default.parse(input2);
370
408
  if (removeMagic) {
371
- output2 = import_path.default.join(outputDir2 != null ? outputDir2 : dir, `${name2}${ext}`);
409
+ output2 = import_path.default.join(outputDir2 ?? dir, `${name}${ext}`);
372
410
  } else if (ext === ".ts") {
373
- output2 = import_path.default.join(outputDir2 != null ? outputDir2 : dir, name2 + ".js");
411
+ output2 = import_path.default.join(outputDir2 ?? dir, name + ".js");
374
412
  } else if (ext === ".tsx") {
375
- output2 = import_path.default.join(outputDir2 != null ? outputDir2 : dir, name2 + ".jsx");
413
+ output2 = import_path.default.join(outputDir2 ?? dir, name + ".jsx");
376
414
  } else if (ext === ".vue") {
377
- output2 = import_path.default.join(outputDir2 != null ? outputDir2 : dir, name2 + ".vue");
415
+ output2 = import_path.default.join(outputDir2 ?? dir, name + ".vue");
378
416
  } else {
379
417
  throw new Error(`Unknwon file extension ${input2}`);
380
418
  }
@@ -391,7 +429,10 @@ var USAGE = `Usage:
391
429
  INPUT Input file or directory
392
430
 
393
431
  OUTPUT Output file or directory
394
- (optional if it can be inferred and won't it overwrite the source file)
432
+ (optional if it can be inferred and it won't overwrite the source file)
433
+
434
+ -t, --remove-ts-comments
435
+ Remove @ts-ignore and @ts-expect-error comments
395
436
 
396
437
  -m, --remove-magic-comments
397
438
  Remove magic comments only, don't perform ts > js transform
@@ -404,6 +445,9 @@ var USAGE = `Usage:
404
445
 
405
446
  Print this help and exit`;
406
447
  var VERSION = package_default.version;
448
+ function unixify(name) {
449
+ return name.replaceAll(import_path.default.sep, "/");
450
+ }
407
451
 
408
452
  // src/cli.ts
409
453
  var args = process.argv.slice(2);
@@ -0,0 +1,40 @@
1
+ import { Options } from 'prettier';
2
+ export { Options as PrettierOptions } from 'prettier';
3
+
4
+ interface RemoveTypeOptions {
5
+ /** Whether to remove ts-ignore and ts-expect-error comments */
6
+ removeTsComments?: boolean;
7
+ }
8
+ interface TransformOptions extends RemoveTypeOptions {
9
+ /** Prettier options */
10
+ prettierOptions?: Options | null;
11
+ }
12
+ /**
13
+ * Transform TypeScript code into vanilla JavaScript without affecting the formatting
14
+ * @param code Source coude
15
+ * @param fileName File name for the source
16
+ * @param options Options
17
+ */
18
+ declare function transform(code: string, fileName: string, options?: TransformOptions): Promise<string>;
19
+ /**
20
+ * Removes magic comments without performing the TS to JS transform
21
+ * @param code Source coude
22
+ * @param fileName File name for the source
23
+ * @param prettierOptions Options to pass to prettier
24
+ */
25
+ declare function removeMagicComments(code: string, fileName: string, prettierOptions?: Options | null): string;
26
+
27
+ /**
28
+ * Transform the input file and write the output to another file
29
+ * @param inputFileName
30
+ * @param outputFileName
31
+ */
32
+ declare function transformFile(inputFileName: string, outputFileName: string, options?: RemoveTypeOptions): Promise<void>;
33
+ /**
34
+ * Remove magic comments from the input file and write the output to another file
35
+ * @param inputFileName
36
+ * @param outputFileName
37
+ */
38
+ declare function removeMagicCommentsFromFile(inputFileName: string, outputFileName: string): Promise<void>;
39
+
40
+ export { removeMagicComments, removeMagicCommentsFromFile, transform, transformFile };
package/dist/index.js CHANGED
@@ -1,26 +1,10 @@
1
+ "use strict";
1
2
  var __create = Object.create;
2
3
  var __defProp = Object.defineProperty;
3
- var __defProps = Object.defineProperties;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
- var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
6
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
- var __getOwnPropSymbols = Object.getOwnPropertySymbols;
8
6
  var __getProtoOf = Object.getPrototypeOf;
9
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
10
- var __propIsEnum = Object.prototype.propertyIsEnumerable;
11
- var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
12
- var __spreadValues = (a, b) => {
13
- for (var prop in b || (b = {}))
14
- if (__hasOwnProp.call(b, prop))
15
- __defNormalProp(a, prop, b[prop]);
16
- if (__getOwnPropSymbols)
17
- for (var prop of __getOwnPropSymbols(b)) {
18
- if (__propIsEnum.call(b, prop))
19
- __defNormalProp(a, prop, b[prop]);
20
- }
21
- return a;
22
- };
23
- var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
24
8
  var __export = (target, all) => {
25
9
  for (var name in all)
26
10
  __defProp(target, name, { get: all[name], enumerable: true });
@@ -33,7 +17,10 @@ var __copyProps = (to, from, except, desc) => {
33
17
  }
34
18
  return to;
35
19
  };
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));
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
37
24
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
38
25
 
39
26
  // src/index.ts
@@ -54,10 +41,12 @@ var import_template_ast_types = require("@vuedx/template-ast-types");
54
41
  var import_preset_typescript = __toESM(require("@babel/preset-typescript"));
55
42
  var import_string_prototype = require("string.prototype.replaceall");
56
43
  (0, import_string_prototype.shim)();
57
- async function transform(code, fileName, prettierOptions) {
44
+ async function transform(code, fileName, options = {}) {
58
45
  var _a, _b, _c, _d;
46
+ const { prettierOptions, ...removeTypeOptions } = options;
59
47
  const originalCode = code;
60
48
  const originalFileName = fileName;
49
+ code = code.replaceAll("\r\n", "\n");
61
50
  if (fileName.endsWith(".vue")) {
62
51
  const parsedVue = (0, import_compiler_sfc.parse)(code, { filename: fileName });
63
52
  if (((_a = parsedVue.descriptor.script) == null ? void 0 : _a.lang) !== "ts" && ((_b = parsedVue.descriptor.scriptSetup) == null ? void 0 : _b.lang) !== "ts") {
@@ -67,18 +56,36 @@ async function transform(code, fileName, prettierOptions) {
67
56
  if (script1 && script2 && script1.loc.start.offset < script2.loc.start.offset) {
68
57
  [script2, script1] = [script1, script2];
69
58
  }
70
- code = await removeTypesFromVueSfcScript(code, fileName, script1, (_c = parsedVue.descriptor.template) == null ? void 0 : _c.ast);
71
- code = await removeTypesFromVueSfcScript(code, fileName, script2, (_d = parsedVue.descriptor.template) == null ? void 0 : _d.ast);
59
+ code = await removeTypesFromVueSfcScript(
60
+ code,
61
+ fileName,
62
+ script1,
63
+ (_c = parsedVue.descriptor.template) == null ? void 0 : _c.ast,
64
+ removeTypeOptions
65
+ );
66
+ code = await removeTypesFromVueSfcScript(
67
+ code,
68
+ fileName,
69
+ script2,
70
+ (_d = parsedVue.descriptor.template) == null ? void 0 : _d.ast,
71
+ removeTypeOptions
72
+ );
72
73
  } else {
73
- code = await removeTypes(code, fileName);
74
+ code = await removeTypes(code, fileName, removeTypeOptions);
74
75
  }
75
- code = (0, import_prettier.format)(code, __spreadProps(__spreadValues({}, prettierOptions), {
76
+ code = (0, import_prettier.format)(code, {
77
+ ...prettierOptions,
76
78
  filepath: originalFileName
77
- }));
79
+ });
78
80
  return code;
79
81
  }
80
- async function removeTypes(code, fileName) {
81
- code = code.replaceAll(/\n\n+/g, "\n/* @detype: empty-line */\n");
82
+ async function removeTypes(code, fileName, options) {
83
+ code = code.replace(
84
+ /\n\n+/g,
85
+ (match) => `
86
+ /* @detype: empty-line=${match.length} */
87
+ `
88
+ );
82
89
  code = processMagicComments(code);
83
90
  const removeComments = {
84
91
  enter(p) {
@@ -110,15 +117,19 @@ async function removeTypes(code, fileName) {
110
117
  ].filter(Boolean),
111
118
  presets: [import_preset_typescript.default],
112
119
  generatorOpts: {
113
- shouldPrintComment: (comment) => comment !== "@detype: remove-me"
120
+ shouldPrintComment: (comment) => comment !== "@detype: remove-me" && (!options.removeTsComments || !comment.match(/^\s*(@ts-ignore|@ts-expect-error)/))
114
121
  }
115
122
  });
116
123
  if (!babelOutput || babelOutput.code === void 0 || babelOutput.code === null) {
117
124
  throw new Error("Babel error");
118
125
  }
119
- return babelOutput.code.replaceAll(/\n\n*/g, "\n").replaceAll("/* @detype: empty-line */", "\n\n");
126
+ return babelOutput.code.replaceAll(/\n\n*/g, "\n").replace(
127
+ /\/\* @detype: empty-line=([0-9]+) \*\//g,
128
+ (_match, p1) => `
129
+ `.repeat(p1 - 2)
130
+ );
120
131
  }
121
- async function removeTypesFromVueSfcScript(code, fileName, script, templateAst) {
132
+ async function removeTypesFromVueSfcScript(code, fileName, script, templateAst, options) {
122
133
  if (script === null || script.lang !== "ts")
123
134
  return code;
124
135
  if (script.setup && templateAst) {
@@ -134,8 +145,10 @@ async function removeTypesFromVueSfcScript(code, fileName, script, templateAst)
134
145
  });
135
146
  script.content += "/* @detype: remove-after-this */" + [...expressions].join(";");
136
147
  }
137
- let scriptCode = await removeTypes(script.content, fileName + ".ts");
138
- const removeAfterIndex = scriptCode.indexOf("/* @detype: remove-after-this */");
148
+ let scriptCode = await removeTypes(script.content, fileName + ".ts", options);
149
+ const removeAfterIndex = scriptCode.indexOf(
150
+ "/* @detype: remove-after-this */"
151
+ );
139
152
  if (removeAfterIndex >= 0) {
140
153
  scriptCode = scriptCode.slice(0, removeAfterIndex);
141
154
  }
@@ -193,9 +206,10 @@ function removeMagicComments(code, fileName, prettierOptions) {
193
206
  start = code.indexOf(REPLACE_COMMENT, before.length + keptText.length);
194
207
  startEnd = start + REPLACE_COMMENT.length;
195
208
  }
196
- code = (0, import_prettier.format)(code, __spreadProps(__spreadValues({}, prettierOptions), {
209
+ code = (0, import_prettier.format)(code, {
210
+ ...prettierOptions,
197
211
  filepath: fileName
198
- }));
212
+ });
199
213
  return code;
200
214
  }
201
215
 
@@ -203,10 +217,13 @@ function removeMagicComments(code, fileName, prettierOptions) {
203
217
  var import_fs = __toESM(require("fs"));
204
218
  var import_prettier2 = require("prettier");
205
219
  var { readFile, writeFile } = import_fs.default.promises;
206
- async function transformFile(inputFileName, outputFileName) {
220
+ async function transformFile(inputFileName, outputFileName, options = {}) {
207
221
  const code = await readFile(inputFileName, "utf-8");
208
- const prettierConfig = await (0, import_prettier2.resolveConfig)(inputFileName);
209
- const output = await transform(code, inputFileName, prettierConfig);
222
+ const prettierOptions = await (0, import_prettier2.resolveConfig)(inputFileName);
223
+ const output = await transform(code, inputFileName, {
224
+ prettierOptions,
225
+ ...options
226
+ });
210
227
  await writeFile(outputFileName, output, "utf-8");
211
228
  }
212
229
  async function removeMagicCommentsFromFile(inputFileName, outputFileName) {
package/index.d.ts CHANGED
@@ -1,47 +1 @@
1
- import { Options as PrettierOptions } from "prettier";
2
-
3
- export { PrettierOptions };
4
-
5
- /**
6
- * Transform TypeScript code into vanilla JavaScript without affecting the formatting
7
- * @param code Source coude
8
- * @param fileName File name for the source
9
- * @param prettierOptions Options to pass to prettier
10
- */
11
- export function transform(
12
- code: string,
13
- fileName: string,
14
- prettierOptions?: PrettierOptions | null | undefined,
15
- ): Promise<string>;
16
-
17
- /**
18
- * Transform the input file and write the output to another file
19
- * @param inputFileName
20
- * @param outputFileName
21
- */
22
- export function transformFile(
23
- inputFileName: string,
24
- outputFileName: string,
25
- ): Promise<void>;
26
-
27
- /**
28
- * Removes magic comments without performing the TS to JS transform
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
43
- */
44
- export function removeMagicCommentsFromFile(
45
- inputFileName: string,
46
- outputFileName: string,
47
- ): Promise<void>;
1
+ export * from "./dist";
package/package.json CHANGED
@@ -1,45 +1,38 @@
1
1
  {
2
2
  "name": "detype",
3
- "version": "0.4.2",
3
+ "version": "0.6.0",
4
4
  "description": "Removes TypeScript type annotations but keeps the formatting",
5
5
  "main": "dist/index.js",
6
6
  "bin": "detype.js",
7
7
  "engines": {
8
- "node": ">=12.22.7"
8
+ "node": ">=14.19.3"
9
9
  },
10
10
  "files": [
11
11
  "dist/**/*",
12
12
  "index.d.ts"
13
13
  ],
14
14
  "dependencies": {
15
- "@babel/core": "^7.17.8",
16
- "@babel/preset-typescript": "^7.16.7",
15
+ "@babel/core": "^7.18.9",
16
+ "@babel/preset-typescript": "^7.18.6",
17
17
  "@vuedx/compiler-sfc": "^0.7.1",
18
- "@vuedx/template-ast-types": "^0.7.2",
18
+ "@vuedx/template-ast-types": "^0.7.3",
19
19
  "fast-glob": "^3.2.11",
20
- "prettier": "^2.6.2",
20
+ "prettier": "^2.7.1",
21
21
  "string.prototype.replaceall": "^1.0.6"
22
22
  },
23
23
  "devDependencies": {
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
- "esbuild-jest": "^0.5.0",
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
- "eslint-plugin-no-only-tests": "^2.6.0",
37
- "eslint-plugin-only-warn": "^1.0.3",
38
- "eslint-plugin-ssr-friendly": "^1.0.6",
39
- "jest": "^27.5.1",
24
+ "@babel/traverse": "^7.18.9",
25
+ "@cyco130/eslint-config": "^2.1.1",
26
+ "@types/babel__core": "^7.1.19",
27
+ "@types/babel__traverse": "^7.17.1",
28
+ "@types/node": "18.6.2",
29
+ "@types/prettier": "^2.6.4",
30
+ "eslint": "^8.20.0",
40
31
  "npm-run-all": "^4.1.5",
41
32
  "rimraf": "^3.0.2",
42
- "typescript": "^4.6.3"
33
+ "tsup": "^6.2.1",
34
+ "typescript": "^4.7.4",
35
+ "vitest": "^0.19.1"
43
36
  },
44
37
  "repository": {
45
38
  "type": "git",
@@ -58,13 +51,14 @@
58
51
  },
59
52
  "homepage": "https://github.com/cyco130/detype#readme",
60
53
  "scripts": {
61
- "build": "node build.mjs",
62
- "watch": "node build.mjs --watch",
54
+ "build": "tsup",
55
+ "dev": "tsup --watch",
56
+ "cq": "pnpm run test:typecheck && pnpm run test:lint && pnpm run test:prettier",
63
57
  "test": "run-p 'test:*'",
64
- "test:unit": "jest",
58
+ "test:unit": "vitest run",
65
59
  "test:typecheck": "tsc -p tsconfig.json --noEmit",
66
60
  "test:lint": "eslint src --max-warnings 0",
67
- "format": "prettier . --write"
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"
61
+ "test:prettier": "prettier --check --ignore-path .gitignore --ignore-unknown . '!pnpm-lock.yaml'",
62
+ "format": "prettier . --write --ignore-path .gitignore --ignore-unknown . '!pnpm-lock.yaml'"
63
+ }
70
64
  }