tsl-dx 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,13 +1,5 @@
1
- import * as tsl0 from "tsl";
1
+ import * as tsl from "tsl";
2
2
 
3
- //#region src/rules/consistent-nullish-comparison.d.ts
4
- /**
5
- * Rule to enforce the use of `== null` or `!= null` for nullish comparisons.
6
- *
7
- * @since 0.0.0
8
- */
9
- declare const consistentNullishComparison: (options?: "off" | undefined) => tsl0.Rule<unknown>;
10
- //#endregion
11
3
  //#region src/rules/no-duplicate-exports.d.ts
12
4
  /**
13
5
  * Rule to detect and merge duplicate `export from` statements from the same module.
@@ -25,7 +17,7 @@ declare const consistentNullishComparison: (options?: "off" | undefined) => tsl0
25
17
  * export { A, B } from 'module';
26
18
  * ```
27
19
  */
28
- declare const noDuplicateExports: (options?: "off" | undefined) => tsl0.Rule<unknown>;
20
+ declare const noDuplicateExports: (options?: "off" | undefined) => tsl.Rule<unknown>;
29
21
  //#endregion
30
22
  //#region src/rules/no-duplicate-imports.d.ts
31
23
  /**
@@ -44,6 +36,12 @@ declare const noDuplicateExports: (options?: "off" | undefined) => tsl0.Rule<unk
44
36
  * import { A, B } from 'module';
45
37
  * ```
46
38
  */
47
- declare const noDuplicateImports: (options?: "off" | undefined) => tsl0.Rule<unknown>;
39
+ declare const noDuplicateImports: (options?: "off" | undefined) => tsl.Rule<unknown>;
40
+ //#endregion
41
+ //#region src/rules/no-multiline-template-expressions-without-auto-dedent.d.ts
42
+ declare const noMultilineTemplateExpressionsWithoutAutoDedent: (options?: "off" | undefined) => tsl.Rule<unknown>;
43
+ //#endregion
44
+ //#region src/rules/nullish.d.ts
45
+ declare const nullish: (options?: "off" | undefined) => tsl.Rule<unknown>;
48
46
  //#endregion
49
- export { consistentNullishComparison, noDuplicateExports, noDuplicateImports };
47
+ export { noDuplicateExports, noDuplicateImports, noMultilineTemplateExpressionsWithoutAutoDedent, nullish };
package/dist/index.js CHANGED
@@ -1,46 +1,9 @@
1
- import { P, match } from "ts-pattern";
2
1
  import { defineRule } from "tsl";
3
2
  import ts, { SyntaxKind } from "typescript";
3
+ import { P, match } from "ts-pattern";
4
4
 
5
- //#region src/rules/consistent-nullish-comparison.ts
6
- /**
7
- * Rule to enforce the use of `== null` or `!= null` for nullish comparisons.
8
- *
9
- * @since 0.0.0
10
- */
11
- const consistentNullishComparison = defineRule(() => ({
12
- name: "local/consistentNullishComparison",
13
- visitor: { BinaryExpression(context, node) {
14
- const newOperatorText = match(node.operatorToken.kind).with(SyntaxKind.EqualsEqualsEqualsToken, () => "==").with(SyntaxKind.ExclamationEqualsEqualsToken, () => "!=").otherwise(() => null);
15
- if (newOperatorText == null) return;
16
- const offendingChild = [node.left, node.right].find((n) => {
17
- switch (n.kind) {
18
- case SyntaxKind.NullKeyword: return true;
19
- case SyntaxKind.Identifier: return n.escapedText === "undefined";
20
- default: return false;
21
- }
22
- });
23
- if (offendingChild == null) return;
24
- context.report({
25
- message: `Use '${newOperatorText}' for nullish comparison.`,
26
- node,
27
- suggestions: [{
28
- message: offendingChild === node.left ? `Replace with 'null ${newOperatorText} ${node.right.getText()}'.` : `Replace with '${node.left.getText()} ${newOperatorText} null'.`,
29
- changes: [{
30
- node: node.operatorToken,
31
- newText: newOperatorText
32
- }, {
33
- node: offendingChild,
34
- newText: "null"
35
- }]
36
- }]
37
- });
38
- } }
39
- }));
40
-
41
- //#endregion
42
5
  //#region src/rules/no-duplicate-exports.ts
43
- const messages$1 = { noDuplicateExports: (p) => `Duplicate export from module ${p.source}.` };
6
+ const messages$3 = { default: (p) => `Duplicate export from module ${p.source}.` };
44
7
  function isReExportDeclaration(node) {
45
8
  return node.exportClause != null && node.moduleSpecifier != null;
46
9
  }
@@ -62,7 +25,7 @@ function isReExportDeclaration(node) {
62
25
  */
63
26
  const noDuplicateExports = defineRule(() => {
64
27
  return {
65
- name: "module/no-duplicate-exports",
28
+ name: "dx/no-duplicate-exports",
66
29
  createData() {
67
30
  return { exports: [] };
68
31
  },
@@ -73,7 +36,7 @@ const noDuplicateExports = defineRule(() => {
73
36
  if (duplicateExport != null) {
74
37
  ctx.report({
75
38
  node,
76
- message: messages$1.noDuplicateExports({ source }),
39
+ message: messages$3.default({ source }),
77
40
  suggestions: buildSuggestions(duplicateExport, node)
78
41
  });
79
42
  return;
@@ -224,7 +187,7 @@ const compose = dual(2, (ab, bc) => (a) => bc(ab(a)));
224
187
 
225
188
  //#endregion
226
189
  //#region src/rules/no-duplicate-imports.ts
227
- const messages = { noDuplicateImports: (p) => `Duplicate import from module ${p.source}.` };
190
+ const messages$2 = { default: (p) => `Duplicate import from module ${p.source}.` };
228
191
  /**
229
192
  * Rule to detect and merge duplicate `import from` statements from the same module.
230
193
  *
@@ -243,7 +206,7 @@ const messages = { noDuplicateImports: (p) => `Duplicate import from module ${p.
243
206
  */
244
207
  const noDuplicateImports = defineRule(() => {
245
208
  return {
246
- name: "module/no-duplicate-imports",
209
+ name: "dx/no-duplicate-imports",
247
210
  createData() {
248
211
  return { imports: [
249
212
  [],
@@ -265,7 +228,7 @@ const noDuplicateImports = defineRule(() => {
265
228
  if (duplicateImport != null) {
266
229
  ctx.report({
267
230
  node,
268
- message: messages.noDuplicateImports({ source: importInfo.source }),
231
+ message: messages$2.default({ source: importInfo.source }),
269
232
  suggestions: importKind > 1 ? [] : [{
270
233
  message: "Merge duplicate imports",
271
234
  changes: [{
@@ -306,4 +269,83 @@ function buildMergedImport(a, b) {
306
269
  }
307
270
 
308
271
  //#endregion
309
- export { consistentNullishComparison, noDuplicateExports, noDuplicateImports };
272
+ //#region src/utils/source-file.ts
273
+ function getLine(node) {
274
+ const sourceFile = node.getSourceFile();
275
+ return [sourceFile.getLineAndCharacterOfPosition(node.getStart()).line, sourceFile.getLineAndCharacterOfPosition(node.getEnd()).line];
276
+ }
277
+
278
+ //#endregion
279
+ //#region src/rules/no-multiline-template-expressions-without-auto-dedent.ts
280
+ const messages$1 = { default: () => `Avoid using multiline template expressions without auto-dedent` };
281
+ const noMultilineTemplateExpressionsWithoutAutoDedent = defineRule(() => ({
282
+ name: "dx/no-multiline-template-expressions-without-auto-dedent",
283
+ visitor: { NoSubstitutionTemplateLiteral(ctx, node) {
284
+ if (node.parent.kind === SyntaxKind.TaggedTemplateExpression) return;
285
+ const [startLine, endLine] = getLine(node);
286
+ if (startLine !== endLine) ctx.report({
287
+ node,
288
+ message: messages$1.default()
289
+ });
290
+ } }
291
+ }));
292
+
293
+ //#endregion
294
+ //#region src/rules/nullish.ts
295
+ const messages = {
296
+ useUnitForUndefined: "Use 'unit' instead of 'undefined'.",
297
+ useLooseNullishComparison: (p) => `Use '${p.op}' for nullish comparison.`
298
+ };
299
+ const suggestions = { replaceWithExpression: (p) => `Replace with '${p.expr}'.` };
300
+ const nullish = defineRule(() => ({
301
+ name: "dx/nullish",
302
+ visitor: {
303
+ UndefinedKeyword(ctx, node) {
304
+ if (node.getSourceFile().isDeclarationFile) return;
305
+ ctx.report({
306
+ node,
307
+ message: messages.useUnitForUndefined,
308
+ suggestions: [{
309
+ message: suggestions.replaceWithExpression({ expr: "unit" }),
310
+ changes: [{
311
+ node,
312
+ newText: "unit"
313
+ }, {
314
+ start: 0,
315
+ end: 0,
316
+ newText: "import { unit } from '@local/eff';\n"
317
+ }]
318
+ }]
319
+ });
320
+ },
321
+ BinaryExpression(ctx, node) {
322
+ const newOperatorText = match(node.operatorToken.kind).with(SyntaxKind.EqualsEqualsEqualsToken, () => "==").with(SyntaxKind.ExclamationEqualsEqualsToken, () => "!=").otherwise(() => null);
323
+ if (newOperatorText == null) return;
324
+ const offendingChild = [node.left, node.right].find((n) => {
325
+ switch (n.kind) {
326
+ case SyntaxKind.NullKeyword: return true;
327
+ case SyntaxKind.Identifier: return n.escapedText === "undefined";
328
+ default: return false;
329
+ }
330
+ });
331
+ if (offendingChild == null) return;
332
+ ctx.report({
333
+ message: messages.useLooseNullishComparison({ op: newOperatorText }),
334
+ node,
335
+ suggestions: [{
336
+ message: suggestions.replaceWithExpression({ expr: offendingChild === node.left ? `null ${newOperatorText} ${node.right.getText()}` : `${node.left.getText()} ${newOperatorText} null` }),
337
+ changes: [{
338
+ node: node.operatorToken,
339
+ newText: newOperatorText
340
+ }, {
341
+ node: offendingChild,
342
+ newText: "null"
343
+ }]
344
+ }]
345
+ });
346
+ }
347
+ }
348
+ }));
349
+
350
+ //#endregion
351
+ export { noDuplicateExports, noDuplicateImports, noMultilineTemplateExpressionsWithoutAutoDedent, nullish };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tsl-dx",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "A tsl plugin for better JavaScript/TypeScript DX.",
5
5
  "keywords": [
6
6
  "tsl",
@@ -25,7 +25,7 @@
25
25
  },
26
26
  "devDependencies": {
27
27
  "dedent": "^1.7.1",
28
- "tsdown": "^0.20.1",
28
+ "tsdown": "^0.20.3",
29
29
  "tsl": "^1.0.28",
30
30
  "vitest": "^4.0.18",
31
31
  "@local/configs": "0.0.0",