wrec 0.40.3 → 0.42.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.
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import fs from 'node:fs';
4
- import path from 'node:path';
5
- import {fileURLToPath} from 'node:url';
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
6
 
7
7
  function fail(message) {
8
8
  console.error(message);
@@ -11,30 +11,28 @@ function fail(message) {
11
11
 
12
12
  function toClassName(tagName) {
13
13
  return tagName
14
- .split('-')
14
+ .split("-")
15
15
  .filter(Boolean)
16
- .map(part => part[0].toUpperCase() + part.slice(1))
17
- .join('');
16
+ .map((part) => part[0].toUpperCase() + part.slice(1))
17
+ .join("");
18
18
  }
19
19
 
20
20
  const [tagName] = process.argv.slice(2);
21
21
 
22
- if (!tagName) fail('usage: npx wrec-scaffold {tag-name}');
23
- if (!tagName.includes('-')) fail('tag name must contain at least one hyphen');
22
+ if (!tagName) fail("usage: npx wrec-scaffold {tag-name}");
23
+ if (!tagName.includes("-")) fail("tag name must contain at least one hyphen");
24
24
 
25
25
  const className = toClassName(tagName);
26
26
  const __filename = fileURLToPath(import.meta.url);
27
27
  const __dirname = path.dirname(__filename);
28
- const templatePath = path.join(__dirname, 'template.ts');
28
+ const templatePath = path.join(__dirname, "template.ts");
29
29
  const outputPath = path.resolve(process.cwd(), `${tagName}.ts`);
30
30
 
31
31
  if (fs.existsSync(outputPath)) {
32
32
  fail(`file already exists: ${outputPath}`);
33
33
  }
34
34
 
35
- const template = fs.readFileSync(templatePath, 'utf8');
36
- const output = template
37
- .replaceAll('{class}', className)
38
- .replaceAll('{tag}', tagName);
35
+ const template = fs.readFileSync(templatePath, "utf8");
36
+ const output = template.replaceAll("{class}", className).replaceAll("{tag}", tagName);
39
37
 
40
38
  fs.writeFileSync(outputPath, output);
@@ -16,22 +16,21 @@
16
16
  // Include the --dry flag for a dry run where `usedBy` values are output,
17
17
  // but no files are modified.
18
18
 
19
- import fs from 'node:fs';
20
- import path from 'node:path';
21
- import ts from 'typescript';
19
+ import fs from "node:fs";
20
+ import path from "node:path";
21
+ import ts from "typescript";
22
22
  import {
23
23
  extendsWrec,
24
24
  getNameText,
25
25
  getPropertyAssignmentNames,
26
26
  getWrecImportInfo,
27
- hasStaticModifier
28
- } from './ast-utils.js';
27
+ hasStaticModifier,
28
+ } from "./ast-utils.js";
29
29
 
30
30
  const cwd = process.cwd();
31
31
  const CALL_RE = /this\.([A-Za-z_$][A-Za-z0-9_$]*)\s*\(/g;
32
- const REFS_RE =
33
- /this\.([A-Za-z_$][A-Za-z0-9_$]*)(\.[A-Za-z_$][A-Za-z0-9_$]*)*/g;
34
- const GETTER_PREFIX = 'get ';
32
+ const REFS_RE = /this\.([A-Za-z_$][A-Za-z0-9_$]*)(\.[A-Za-z_$][A-Za-z0-9_$]*)*/g;
33
+ const GETTER_PREFIX = "get ";
35
34
 
36
35
  // Records property names introduced by
37
36
  // an identifier or object binding pattern.
@@ -65,7 +64,7 @@ function addObjectBindingProps(props, bindingPattern) {
65
64
  // This is the heart of the functionality in this script.
66
65
  function analyzeSourceFile(sourceFile) {
67
66
  const edits = [];
68
- const {names: wrecNames, quote} = getWrecImportInfo(sourceFile);
67
+ const { names: wrecNames, quote } = getWrecImportInfo(sourceFile);
69
68
  const suggestions = [];
70
69
  let foundWrecSubclass = false;
71
70
 
@@ -80,7 +79,7 @@ function analyzeSourceFile(sourceFile) {
80
79
  if (
81
80
  ts.isPropertyDeclaration(member) &&
82
81
  hasStaticModifier(member) &&
83
- getNameText(member.name) === 'properties' &&
82
+ getNameText(member.name) === "properties" &&
84
83
  member.initializer &&
85
84
  ts.isObjectLiteralExpression(member.initializer)
86
85
  ) {
@@ -107,37 +106,30 @@ function analyzeSourceFile(sourceFile) {
107
106
  // Skip the member if we can't gets its name
108
107
  // or if its value isn't an object literal.
109
108
  const propName = getNameText(member.name);
110
- if (!propName || !ts.isObjectLiteralExpression(member.initializer))
111
- continue;
109
+ if (!propName || !ts.isObjectLiteralExpression(member.initializer)) continue;
112
110
 
113
111
  // Convert the Set of methods that use the property into a sorted array.
114
- const methodNames = [
115
- ...(propToMethods.get(propName) ?? new Set())
116
- ].sort();
112
+ const methodNames = [...(propToMethods.get(propName) ?? new Set())].sort();
117
113
 
118
114
  // Get an array of all the NodeObjects that represent
119
115
  // properties in the configuration object, except the one for "usedBy".
120
116
  const configObject = member.initializer;
121
117
  const existingMembers = configObject.properties.filter(
122
- property =>
123
- !(
124
- ts.isPropertyAssignment(property) &&
125
- getNameText(property.name) === 'usedBy'
126
- )
118
+ (property) =>
119
+ !(ts.isPropertyAssignment(property) && getNameText(property.name) === "usedBy"),
127
120
  );
128
121
 
129
122
  // If the property is used by any methods ...
130
123
  if (methodNames.length > 0) {
131
124
  suggestions.push({
132
125
  propName,
133
- suggestion: createUsedByProperty(methodNames, quote)
126
+ suggestion: createUsedByProperty(methodNames, quote),
134
127
  });
135
128
  } else {
136
129
  // Determine if the configuration object already had a "usedBy" property.
137
- const hadUsedBy =
138
- existingMembers.length !== configObject.properties.length;
130
+ const hadUsedBy = existingMembers.length !== configObject.properties.length;
139
131
  if (hadUsedBy) {
140
- suggestions.push({propName, suggestion: 'remove usedBy'});
132
+ suggestions.push({ propName, suggestion: "remove usedBy" });
141
133
  } else {
142
134
  continue;
143
135
  }
@@ -148,7 +140,7 @@ function analyzeSourceFile(sourceFile) {
148
140
  // Get the existing text for the property configuration object.
149
141
  const currentText = sourceFile.text.slice(
150
142
  configObject.getStart(sourceFile),
151
- configObject.end
143
+ configObject.end,
152
144
  );
153
145
  // If they differ, add an object describing
154
146
  // the desired edit to the edits array.
@@ -158,7 +150,7 @@ function analyzeSourceFile(sourceFile) {
158
150
  end: configObject.end,
159
151
  propName,
160
152
  oldText: currentText,
161
- text: nextText
153
+ text: nextText,
162
154
  });
163
155
  }
164
156
  }
@@ -171,7 +163,7 @@ function analyzeSourceFile(sourceFile) {
171
163
  edits: [],
172
164
  foundWrecSubclass: false,
173
165
  suggestions,
174
- text: sourceFile.text
166
+ text: sourceFile.text,
175
167
  };
176
168
  }
177
169
 
@@ -183,7 +175,7 @@ function analyzeSourceFile(sourceFile) {
183
175
  edits: [],
184
176
  foundWrecSubclass: true,
185
177
  suggestions,
186
- text: sourceFile.text
178
+ text: sourceFile.text,
187
179
  };
188
180
  }
189
181
 
@@ -195,8 +187,7 @@ function analyzeSourceFile(sourceFile) {
195
187
 
196
188
  // Make each of the edits to the source file.
197
189
  for (const edit of edits) {
198
- nextSource =
199
- nextSource.slice(0, edit.start) + edit.text + nextSource.slice(edit.end);
190
+ nextSource = nextSource.slice(0, edit.start) + edit.text + nextSource.slice(edit.end);
200
191
  }
201
192
 
202
193
  return {
@@ -204,7 +195,7 @@ function analyzeSourceFile(sourceFile) {
204
195
  edits,
205
196
  foundWrecSubclass: true,
206
197
  suggestions,
207
- text: nextSource
198
+ text: nextSource,
208
199
  };
209
200
  }
210
201
 
@@ -220,17 +211,13 @@ function buildConfigText(sourceFile, member, methodNames, quote) {
220
211
  // Get an array of AST nodes for all the config object properties
221
212
  // except `usedBy`.
222
213
  const existingMembers = configObject.properties.filter(
223
- property =>
224
- !(
225
- ts.isPropertyAssignment(property) &&
226
- getNameText(property.name) === 'usedBy'
227
- )
214
+ (property) => !(ts.isPropertyAssignment(property) && getNameText(property.name) === "usedBy"),
228
215
  );
229
216
 
230
217
  // Create property assignment strings from the AST nodes.
231
- const {text} = sourceFile;
232
- const propertyStrings = existingMembers.map(property =>
233
- text.slice(property.getStart(sourceFile), property.end).trim()
218
+ const { text } = sourceFile;
219
+ const propertyStrings = existingMembers.map((property) =>
220
+ text.slice(property.getStart(sourceFile), property.end).trim(),
234
221
  );
235
222
 
236
223
  // If this property is used by any methods,
@@ -239,12 +226,9 @@ function buildConfigText(sourceFile, member, methodNames, quote) {
239
226
  propertyStrings.push(createUsedByProperty(methodNames, quote));
240
227
  }
241
228
 
242
- const existingPropertiesString = text.slice(
243
- configObject.getStart(sourceFile),
244
- configObject.end
245
- );
229
+ const existingPropertiesString = text.slice(configObject.getStart(sourceFile), configObject.end);
246
230
 
247
- const multiline = existingPropertiesString.includes('\n');
231
+ const multiline = existingPropertiesString.includes("\n");
248
232
  if (multiline) {
249
233
  // Build and return a multi-line config object string
250
234
  // that preserves the existing indentation.
@@ -252,10 +236,8 @@ function buildConfigText(sourceFile, member, methodNames, quote) {
252
236
  const [firstMember] = existingMembers;
253
237
  const propertyIndent = firstMember
254
238
  ? getIndent(text, firstMember.getStart(sourceFile))
255
- : memberIndent + ' ';
256
- const content = propertyStrings
257
- .map(part => `${propertyIndent}${part}`)
258
- .join(',\n');
239
+ : memberIndent + " ";
240
+ const content = propertyStrings.map((part) => `${propertyIndent}${part}`).join(",\n");
259
241
  return `{\n${content}\n${memberIndent}}`;
260
242
  }
261
243
 
@@ -263,19 +245,16 @@ function buildConfigText(sourceFile, member, methodNames, quote) {
263
245
  // that preserves the existing spacing around curly braces.
264
246
  const openMatch = existingPropertiesString.match(/^\{(\s*)/);
265
247
  const closeMatch = existingPropertiesString.match(/(\s*)\}$/);
266
- const openSpacing = openMatch ? openMatch[1] : ' ';
267
- const closeSpacing = closeMatch ? closeMatch[1] : ' ';
268
- return `{${openSpacing}${propertyStrings.join(', ')}${closeSpacing}}`;
248
+ const openSpacing = openMatch ? openMatch[1] : " ";
249
+ const closeSpacing = closeMatch ? closeMatch[1] : " ";
250
+ return `{${openSpacing}${propertyStrings.join(", ")}${closeSpacing}}`;
269
251
  }
270
252
 
271
253
  // Walks a method or accessor body to collect component property reads
272
254
  // and dependency targets reached through `this`.
273
255
  // This is only called by getMethodUsages.
274
256
  function collectMethodBodyUsage(node, getterNames, props, calledMethods) {
275
- if (
276
- ts.isPropertyAccessExpression(node) &&
277
- node.expression.kind === ts.SyntaxKind.ThisKeyword
278
- ) {
257
+ if (ts.isPropertyAccessExpression(node) && node.expression.kind === ts.SyntaxKind.ThisKeyword) {
279
258
  // Handles direct property access like `this.foo`
280
259
  // and records method calls like `this.foo()`.
281
260
  recordThisAccess(props, calledMethods, getterNames, node, node.name.text);
@@ -287,13 +266,7 @@ function collectMethodBodyUsage(node, getterNames, props, calledMethods) {
287
266
  ) {
288
267
  // Handles string-based element access like `this['foo']`
289
268
  // and records method calls like `this['foo']()`.
290
- recordThisAccess(
291
- props,
292
- calledMethods,
293
- getterNames,
294
- node,
295
- node.argumentExpression.text
296
- );
269
+ recordThisAccess(props, calledMethods, getterNames, node, node.argumentExpression.text);
297
270
  } else if (
298
271
  ts.isVariableDeclaration(node) &&
299
272
  node.initializer &&
@@ -311,10 +284,7 @@ function collectMethodBodyUsage(node, getterNames, props, calledMethods) {
311
284
  // or object-literal patterns that pull named properties from it.
312
285
  if (ts.isObjectLiteralExpression(node.left)) {
313
286
  for (const property of node.left.properties) {
314
- if (
315
- ts.isShorthandPropertyAssignment(property) ||
316
- ts.isPropertyAssignment(property)
317
- ) {
287
+ if (ts.isShorthandPropertyAssignment(property) || ts.isPropertyAssignment(property)) {
318
288
  const name = getNameText(property.name);
319
289
  if (name) props.add(name);
320
290
  }
@@ -324,8 +294,8 @@ function collectMethodBodyUsage(node, getterNames, props, calledMethods) {
324
294
  }
325
295
  }
326
296
 
327
- ts.forEachChild(node, child =>
328
- collectMethodBodyUsage(child, getterNames, props, calledMethods)
297
+ ts.forEachChild(node, (child) =>
298
+ collectMethodBodyUsage(child, getterNames, props, calledMethods),
329
299
  );
330
300
  }
331
301
 
@@ -334,7 +304,7 @@ function createUsedByProperty(methodNames, quote) {
334
304
  const value =
335
305
  methodNames.length === 1
336
306
  ? `${quote}${methodNames[0]}${quote}`
337
- : `[${methodNames.map(name => `${quote}${name}${quote}`).join(', ')}]`;
307
+ : `[${methodNames.map((name) => `${quote}${name}${quote}`).join(", ")}]`;
338
308
  return `usedBy: ${value}`;
339
309
  }
340
310
 
@@ -342,23 +312,23 @@ function createUsedByProperty(methodNames, quote) {
342
312
  // the usedBy properties in property configuration objects
343
313
  // found in the file at a given relative path.
344
314
  export function evaluateSourceFile(filePath, options = {}) {
345
- const {dry = false} = options;
315
+ const { dry = false } = options;
346
316
  const absFilePath = path.resolve(cwd, filePath);
347
317
  validateFile(absFilePath);
348
318
 
349
319
  // The file is read in this function instead of in evaluateSourceText
350
320
  // so unit tests can pass hard-coded text to that function.
351
- const text = fs.readFileSync(absFilePath, 'utf8');
321
+ const text = fs.readFileSync(absFilePath, "utf8");
352
322
  let {
353
323
  changed,
354
324
  foundWrecSubclass,
355
325
  suggestions,
356
- text: nextText
326
+ text: nextText,
357
327
  } = evaluateSourceText(absFilePath, text);
358
328
 
359
329
  // If we didn't find the definition of a class that extends Wrec ...
360
330
  if (!foundWrecSubclass) {
361
- throw new Error('No class extending Wrec was found.');
331
+ throw new Error("No class extending Wrec was found.");
362
332
  }
363
333
 
364
334
  // If this isn't a dry run and changes were made,
@@ -368,7 +338,7 @@ export function evaluateSourceFile(filePath, options = {}) {
368
338
  suggestions = []; // all the suggestions have been applied
369
339
  }
370
340
 
371
- return {changed, foundWrecSubclass, suggestions, text: nextText};
341
+ return { changed, foundWrecSubclass, suggestions, text: nextText };
372
342
  }
373
343
 
374
344
  // Determines what changes, if any, should be made in
@@ -377,16 +347,8 @@ export function evaluateSourceFile(filePath, options = {}) {
377
347
  // This function was factored out of evaluateSourceFile
378
348
  // to support unit tests.
379
349
  export function evaluateSourceText(filePath, text) {
380
- const scriptKind = filePath.endsWith('.ts')
381
- ? ts.ScriptKind.TS
382
- : ts.ScriptKind.JS;
383
- const sourceFile = ts.createSourceFile(
384
- filePath,
385
- text,
386
- ts.ScriptTarget.Latest,
387
- true,
388
- scriptKind
389
- );
350
+ const scriptKind = filePath.endsWith(".ts") ? ts.ScriptKind.TS : ts.ScriptKind.JS;
351
+ const sourceFile = ts.createSourceFile(filePath, text, ts.ScriptTarget.Latest, true, scriptKind);
390
352
  return analyzeSourceFile(sourceFile);
391
353
  }
392
354
 
@@ -403,11 +365,11 @@ function getCssCalledMethods(classNode) {
403
365
  if (
404
366
  ts.isPropertyDeclaration(member) &&
405
367
  hasStaticModifier(member) &&
406
- getNameText(member.name) === 'css' &&
368
+ getNameText(member.name) === "css" &&
407
369
  member.initializer &&
408
370
  ts.isTaggedTemplateExpression(member.initializer) &&
409
371
  ts.isIdentifier(member.initializer.tag) &&
410
- member.initializer.tag.text === 'css'
372
+ member.initializer.tag.text === "css"
411
373
  ) {
412
374
  // Keep the last matching declaration because
413
375
  // JavaScript class fields use last-one-wins semantics
@@ -441,7 +403,7 @@ function getComputedCalledMethods(classNode) {
441
403
  if (
442
404
  ts.isPropertyDeclaration(member) &&
443
405
  hasStaticModifier(member) &&
444
- getNameText(member.name) === 'properties' &&
406
+ getNameText(member.name) === "properties" &&
445
407
  member.initializer &&
446
408
  ts.isObjectLiteralExpression(member.initializer)
447
409
  ) {
@@ -459,10 +421,7 @@ function getComputedCalledMethods(classNode) {
459
421
  for (const property of propertiesNode.properties) {
460
422
  // If it isn't a property assignment or
461
423
  // the property value isn't an object literal then skip it.
462
- if (
463
- !ts.isPropertyAssignment(property) ||
464
- !ts.isObjectLiteralExpression(property.initializer)
465
- ) {
424
+ if (!ts.isPropertyAssignment(property) || !ts.isObjectLiteralExpression(property.initializer)) {
466
425
  continue;
467
426
  }
468
427
 
@@ -472,7 +431,7 @@ function getComputedCalledMethods(classNode) {
472
431
  // the property name isn't "computed" then skip it.
473
432
  if (
474
433
  !ts.isPropertyAssignment(configProperty) ||
475
- getNameText(configProperty.name) !== 'computed'
434
+ getNameText(configProperty.name) !== "computed"
476
435
  ) {
477
436
  continue;
478
437
  }
@@ -512,9 +471,9 @@ function getGetterDependency(name) {
512
471
  // Returns the leading indentation in the line
513
472
  // that begins at a given position (`pos`) inside `text`.
514
473
  function getIndent(text, pos) {
515
- const lineStart = text.lastIndexOf('\n', pos - 1) + 1;
474
+ const lineStart = text.lastIndexOf("\n", pos - 1) + 1;
516
475
  const match = /^[ \t]*/.exec(text.slice(lineStart));
517
- return match ? match[0] : '';
476
+ return match ? match[0] : "";
518
477
  }
519
478
 
520
479
  // Returns a map where the keys are property names and
@@ -543,7 +502,7 @@ function getMethodUsages(classNode, propertyNames) {
543
502
  methodInfo.set(methodName, {
544
503
  calledMethods,
545
504
  isPrivate: ts.isPrivateIdentifier(member.name),
546
- props
505
+ props,
547
506
  });
548
507
  }
549
508
 
@@ -553,7 +512,7 @@ function getMethodUsages(classNode, propertyNames) {
553
512
  const entryMethods = new Set([
554
513
  ...getCssCalledMethods(classNode),
555
514
  ...getTemplateCalledMethods(classNode),
556
- ...getComputedCalledMethods(classNode)
515
+ ...getComputedCalledMethods(classNode),
557
516
  ]);
558
517
  const memo = new Map();
559
518
 
@@ -589,14 +548,14 @@ function getTemplateCalledMethods(classNode) {
589
548
  if (
590
549
  ts.isPropertyDeclaration(member) &&
591
550
  hasStaticModifier(member) &&
592
- getNameText(member.name) === 'html' &&
551
+ getNameText(member.name) === "html" &&
593
552
  member.initializer
594
553
  ) {
595
554
  // If the value is a tagged template literal with the "html" tag ...
596
555
  if (
597
556
  ts.isTaggedTemplateExpression(member.initializer) &&
598
557
  ts.isIdentifier(member.initializer.tag) &&
599
- member.initializer.tag.text === 'html'
558
+ member.initializer.tag.text === "html"
600
559
  ) {
601
560
  template = member.initializer.template;
602
561
  }
@@ -628,12 +587,7 @@ function getTransitiveProps(methodInfo, memo, methodName, seen = new Set()) {
628
587
 
629
588
  if (info) {
630
589
  for (const calledMethod of info.calledMethods) {
631
- const calledProps = getTransitiveProps(
632
- methodInfo,
633
- memo,
634
- calledMethod,
635
- seen
636
- );
590
+ const calledProps = getTransitiveProps(methodInfo, memo, calledMethod, seen);
637
591
  for (const propName of calledProps) props.add(propName);
638
592
  }
639
593
  }
@@ -666,32 +620,35 @@ function isInstanceMethodMember(member) {
666
620
  function isSupportedSourceFile(filePath, excludeTests = false) {
667
621
  return (
668
622
  /\.(js|ts)$/.test(filePath) &&
669
- !/\.d\.ts$/.test(filePath) &&
670
- (!excludeTests || !filePath.includes('.test.'))
623
+ !filePath.endsWith(".d.ts") &&
624
+ (!excludeTests || !filePath.includes(".test."))
671
625
  );
672
626
  }
673
627
 
674
628
  // Handles CLI arguments and runs the script.
675
629
  function main() {
676
630
  const args = process.argv.slice(2);
677
- const unknownFlags = args.filter(
678
- arg => arg.startsWith('--') && arg !== '--dry'
679
- );
631
+ const unknownFlags = args.filter((arg) => arg.startsWith("--") && arg !== "--dry");
680
632
  if (unknownFlags.length > 0) {
681
633
  throw new Error(`unknown option: ${unknownFlags[0]}`);
682
634
  }
683
635
 
684
- const inputPaths = args.filter(arg => !arg.startsWith('--'));
636
+ const inputPaths = args.filter((arg) => !arg.startsWith("--"));
685
637
 
686
638
  if (inputPaths.length !== 1) {
687
- throw new Error('Specify a single source file');
639
+ throw new Error("Specify a single source file");
640
+ }
641
+
642
+ const inputPath = inputPaths[0];
643
+ if (!isSupportedSourceFile(inputPath)) {
644
+ throw new Error("unsupported file type");
688
645
  }
689
646
 
690
- const dry = args.includes('--dry');
691
- const result = evaluateSourceFile(inputPaths[0], {dry});
647
+ const dry = args.includes("--dry");
648
+ const result = evaluateSourceFile(inputPath, { dry });
692
649
  if (dry) {
693
650
  // Report the proposed changes.
694
- for (const {propName, suggestion} of result.suggestions) {
651
+ for (const { propName, suggestion } of result.suggestions) {
695
652
  console.info(`${propName} - ${suggestion}`);
696
653
  }
697
654
 
@@ -699,9 +656,9 @@ function main() {
699
656
  // so that condition can be checked.
700
657
  if (result.changed) process.exit(1);
701
658
  } else if (result.changed) {
702
- console.info('updated source file');
659
+ console.info("updated source file");
703
660
  } else {
704
- console.info('no changes needed');
661
+ console.info("no changes needed");
705
662
  }
706
663
  }
707
664
 
@@ -718,13 +675,13 @@ function recordThisAccess(props, calledMethods, getterNames, node, name) {
718
675
 
719
676
  // Validates that a source file exists and has a supported extension.
720
677
  function validateFile(absFilePath) {
721
- if (!fs.existsSync(absFilePath)) throw new Error('File not found');
678
+ if (!fs.existsSync(absFilePath)) throw new Error("File not found");
722
679
 
723
680
  const stat = fs.statSync(absFilePath);
724
- if (!stat.isFile()) throw new Error('Not a file');
681
+ if (!stat.isFile()) throw new Error("Not a file");
725
682
 
726
683
  if (!/\.(js|ts)$/.test(absFilePath)) {
727
- throw new Error('Unsupported file type');
684
+ throw new Error("Unsupported file type");
728
685
  }
729
686
  }
730
687