@pezkuwi/dev 0.86.3 → 0.86.5

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,4 +1,4 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.packageInfo = void 0;
4
- exports.packageInfo = { name: '@pezkuwi/dev', path: typeof __dirname === 'string' ? __dirname : 'auto', type: 'cjs', version: '0.86.3' };
4
+ exports.packageInfo = { name: '@pezkuwi/dev', path: typeof __dirname === 'string' ? __dirname : 'auto', type: 'cjs', version: '0.86.5' };
@@ -0,0 +1,78 @@
1
+ // Copyright 2017-2026 @pezkuwi/dev authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ /** @type {Map<string, string>} */
5
+ const sources = new Map();
6
+
7
+ /**
8
+ * Widens the fix eslint core gives an unused disable directive.
9
+ *
10
+ * Core removes only the comment and puts a single space in its place
11
+ * (lib/linter/apply-disable-directives.js), so --fix turns
12
+ *
13
+ * const a = 1;
14
+ * // eslint-disable-next-line no-console
15
+ * const b = 2;
16
+ *
17
+ * into a whitespace-only line between the two statements, which the layout
18
+ * rules then leave behind as a blank line. Measured on the consumers after the
19
+ * eslint 10 move: 23 such lines in api alone, each removed by hand.
20
+ *
21
+ * Only a fix that removes a whole directive comment is changed: alone on its
22
+ * line, the line goes; after code, the space before it goes; before code, the
23
+ * space after it goes. Removing one rule name out of several is left as core
24
+ * makes it, as is every message with a rule id.
25
+ *
26
+ * @param {import('eslint').Linter.LintMessage} message
27
+ * @param {string} text the source the message was produced from
28
+ * @returns {import('eslint').Linter.LintMessage}
29
+ */
30
+ function widenDirectiveFix (message, text) {
31
+ if (message.ruleId !== null || message.fix?.text.trim() !== '') {
32
+ return message;
33
+ }
34
+
35
+ let [start, end] = message.fix.range;
36
+
37
+ if (!/^\/[/*]/.test(text.slice(start, end))) {
38
+ return message;
39
+ }
40
+
41
+ const lineStart = text.lastIndexOf('\n', start - 1) + 1;
42
+ const newline = text.indexOf('\n', end);
43
+ const lineEnd = newline === -1 ? text.length : newline;
44
+ const aloneBefore = text.slice(lineStart, start).trim() === '';
45
+ const aloneAfter = text.slice(end, lineEnd).trim() === '';
46
+
47
+ if (aloneBefore && aloneAfter) {
48
+ return { ...message, fix: { range: [lineStart, newline === -1 ? text.length : newline + 1], text: '' } };
49
+ } else if (aloneBefore) {
50
+ while (end < lineEnd && /[ \t]/.test(text[end] ?? '')) {
51
+ end++;
52
+ }
53
+ } else {
54
+ while (start > lineStart && /[ \t]/.test(text[start - 1] ?? '')) {
55
+ start--;
56
+ }
57
+ }
58
+
59
+ return { ...message, fix: { range: [start, end], text: '' } };
60
+ }
61
+
62
+ /** @type {import('eslint').Linter.Processor} */
63
+ export const directiveLines = {
64
+ meta: { name: '@pezkuwi/dev/directive-lines' },
65
+ postprocess (lists, filename) {
66
+ const text = sources.get(filename) ?? '';
67
+
68
+ sources.delete(filename);
69
+
70
+ return lists.flat().map((m) => widenDirectiveFix(m, text));
71
+ },
72
+ preprocess (text, filename) {
73
+ sources.set(filename, text);
74
+
75
+ return [text];
76
+ },
77
+ supportsAutofix: true
78
+ };
package/config/eslint.js CHANGED
@@ -22,9 +22,11 @@ import simpleImportSortPlugin from 'eslint-plugin-simple-import-sort';
22
22
  import sortDestructureKeysPlugin from 'eslint-plugin-sort-destructure-keys';
23
23
  import globals from 'globals';
24
24
 
25
+ import { directiveLines } from './eslint.directives.js';
25
26
  import { overrideAll, overrideJs, overrideJsx, overrideSpec } from './eslint.rules.js';
26
27
  import { standardRules } from './eslint.standard.js';
27
28
  import { withoutContextualAssertions } from './eslint.typeAssertion.js';
29
+ import { withoutExportAllFix } from './eslint.typeExports.js';
28
30
 
29
31
  /**
30
32
  * eslint-plugin-header declares no options schema, and eslint 9+ rejects
@@ -94,8 +96,8 @@ export default [
94
96
  },
95
97
  plugins: {
96
98
  '@stylistic': stylisticPlugin,
97
- // see eslint.typeAssertion.js
98
- '@typescript-eslint': withoutContextualAssertions(tsPlugin),
99
+ // see eslint.typeAssertion.js and eslint.typeExports.js
100
+ '@typescript-eslint': withoutExportAllFix(withoutContextualAssertions(tsPlugin)),
99
101
  // eslint-plugin-header reports at the first token after the leading
100
102
  // comments, so a generated file's /* eslint-disable */ covers it; the
101
103
  // maintained fork reports at line 1 instead. The compat layer supplies the
@@ -126,6 +128,8 @@ export default [
126
128
  },
127
129
  {
128
130
  files: extsToGlobs(EXT_ALL),
131
+ // see eslint.directives.js
132
+ processor: directiveLines,
129
133
  rules: {
130
134
  ...eslintJs.configs.recommended.rules,
131
135
  ...standardRules,
@@ -0,0 +1,63 @@
1
+ // Copyright 2017-2026 @pezkuwi/dev authors & contributors
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ const RULE = 'consistent-type-exports';
5
+
6
+ /**
7
+ * consistent-type-exports reports an `export * from './x'` whose module only
8
+ * exports types, and its autofix turns it into `export type * from './x'`.
9
+ * That changes what runs: `export *` loads the module, `export type *` is
10
+ * erased, so a module that only exports types but registers something when it
11
+ * loads (a Handlebars partial, an interface augmentation) silently stops being
12
+ * loaded. Measured on the consumers: the api typegen lost its `docs` partial
13
+ * this way and the build failed only at template time.
14
+ *
15
+ * Named re-exports do not have this problem: under verbatimModuleSyntax a
16
+ * type re-exported without `type` is already a compile error (TS1205).
17
+ *
18
+ * This keeps the report as it is and moves the `export *` fix into a
19
+ * suggestion, so --fix no longer applies it and an editor still offers it.
20
+ * Whoever accepts it decides whether the module needs a side-effect import.
21
+ *
22
+ * @template {object} P
23
+ * @param {P} plugin the @typescript-eslint plugin
24
+ * @returns {P}
25
+ */
26
+ export function withoutExportAllFix (plugin) {
27
+ const rules = /** @type {Record<string, import('eslint').Rule.RuleModule>} */ (/** @type {{ rules: unknown }} */ (plugin).rules);
28
+ const rule = rules[RULE];
29
+
30
+ return /** @type {P} */ ({
31
+ ...plugin,
32
+ rules: {
33
+ ...rules,
34
+ [RULE]: {
35
+ ...rule,
36
+ /** @param {import('eslint').Rule.RuleContext} context */
37
+ create (context) {
38
+ const filtered = Object.create(context, {
39
+ report: {
40
+ value: (/** @type {import('eslint').Rule.ReportDescriptor} */ descriptor) => {
41
+ if ('node' in descriptor && descriptor.node.type === 'ExportAllDeclaration' && descriptor.fix) {
42
+ const { fix, ...rest } = descriptor;
43
+
44
+ context.report({
45
+ ...rest,
46
+ suggest: [{ desc: 'Use `export type *` (the module is no longer loaded: add a side-effect import if it registers anything)', fix }]
47
+ });
48
+
49
+ return;
50
+ }
51
+
52
+ context.report(descriptor);
53
+ }
54
+ }
55
+ });
56
+
57
+ return rule.create(filtered);
58
+ },
59
+ meta: { ...rule.meta, hasSuggestions: true }
60
+ }
61
+ }
62
+ });
63
+ }
package/package.json CHANGED
@@ -15,7 +15,7 @@
15
15
  },
16
16
  "sideEffects": false,
17
17
  "type": "module",
18
- "version": "0.86.3",
18
+ "version": "0.86.5",
19
19
  "bin": {
20
20
  "pezkuwi-ci-ghact-build": "./scripts/pezkuwi-ci-ghact-build.mjs",
21
21
  "pezkuwi-ci-ghact-docs": "./scripts/pezkuwi-ci-ghact-docs.mjs",
@@ -63,9 +63,11 @@
63
63
  }
64
64
  },
65
65
  "./config/eslint": "./config/eslint.js",
66
+ "./config/eslint.directives": "./config/eslint.directives.js",
66
67
  "./config/eslint.rules": "./config/eslint.rules.js",
67
68
  "./config/eslint.standard": "./config/eslint.standard.js",
68
69
  "./config/eslint.typeAssertion": "./config/eslint.typeAssertion.js",
70
+ "./config/eslint.typeExports": "./config/eslint.typeExports.js",
69
71
  "./config/prettier.cjs": "./config/prettier.cjs",
70
72
  "./config/prettier": "./config/prettier.cjs",
71
73
  "./config/rollup": "./config/rollup.js",
@@ -289,8 +291,8 @@
289
291
  "dependencies": {
290
292
  "@eslint/compat": "^2.1.1",
291
293
  "@eslint/js": "^10.0.1",
292
- "@pezkuwi/dev-test": "^0.86.3",
293
- "@pezkuwi/dev-ts": "^0.86.3",
294
+ "@pezkuwi/dev-test": "^0.86.5",
295
+ "@pezkuwi/dev-ts": "^0.86.5",
294
296
  "@rollup/plugin-alias": "^5.1.1",
295
297
  "@rollup/plugin-commonjs": "^29.0.3",
296
298
  "@rollup/plugin-dynamic-import-vars": "^2.1.5",
package/packageInfo.js CHANGED
@@ -1 +1 @@
1
- export const packageInfo = { name: '@pezkuwi/dev', path: (import.meta && import.meta.url) ? new URL(import.meta.url).pathname.substring(0, new URL(import.meta.url).pathname.lastIndexOf('/') + 1) : 'auto', type: 'esm', version: '0.86.3' };
1
+ export const packageInfo = { name: '@pezkuwi/dev', path: (import.meta && import.meta.url) ? new URL(import.meta.url).pathname.substring(0, new URL(import.meta.url).pathname.lastIndexOf('/') + 1) : 'auto', type: 'esm', version: '0.86.5' };
@@ -7,8 +7,6 @@ import yargs from 'yargs';
7
7
 
8
8
  import { __dirname, execPm, GITHUB_REPO, logBin } from './util.mjs';
9
9
 
10
- const TS_CONFIG_BUILD = true;
11
-
12
10
  logBin('pezkuwi-dev-run-lint');
13
11
 
14
12
  // Since yargs can also be a promise, we just relax the type here completely
@@ -36,5 +34,11 @@ if (!argv['skip-eslint']) {
36
34
  }
37
35
 
38
36
  if (!argv['skip-tsc']) {
39
- execPm(`pezkuwi-exec-tsc --noEmit --emitDeclarationOnly false --pretty${TS_CONFIG_BUILD ? ' --project tsconfig.build.json' : ''}`);
37
+ // The root tsconfig.build.json has `files: []` and only references the
38
+ // packages, so `tsc --project` on it checks no file at all. --build follows
39
+ // the references; it cannot be combined with --noEmit, so it writes the
40
+ // declarations the build would. Previous build output is removed first, since
41
+ // package configs without an `include` would otherwise pick it up as source.
42
+ execPm('pezkuwi-dev-clean-build');
43
+ execPm('pezkuwi-exec-tsc --build tsconfig.build.json --pretty');
40
44
  }