@konvert7/klint 0.19.2 → 0.20.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 +1 -0
- package/cli.ts +1 -0
- package/core/arch.ts +10 -11
- package/core/paths.ts +9 -0
- package/core/rule-helpers.ts +2 -2
- package/core/runner.ts +7 -7
- package/package.json +5 -5
- package/rules/no-consecutive-array-push.ts +2 -2
- package/rules/no-nested-template-literals.ts +2 -2
- package/rules/no-object-in-template.ts +1 -1
- package/rules/no-single-char-class.ts +2 -2
- package/rules/no-string-match.ts +2 -2
- package/rules/no-sync-in-async.ts +2 -2
- package/rules/no-unguarded-json-parse.ts +2 -2
- package/rules/prefer-at.ts +2 -2
- package/rules/prefer-nullish-coalescing-assign.ts +2 -2
- package/rules/prefer-string-raw-regexp.ts +2 -2
- package/rules/prefer-string-raw.ts +2 -2
- package/rules/prefer-string-replaceall.ts +3 -3
- package/tools/semantic-release-root-npm.js +15 -6
package/README.md
CHANGED
package/cli.ts
CHANGED
package/core/arch.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import { dirname, isAbsolute,
|
|
1
|
+
import { dirname, isAbsolute, resolve } from "node:path";
|
|
2
2
|
import ts from "typescript";
|
|
3
3
|
import { walkAst } from "./ast";
|
|
4
|
+
import { relativeSlashPath, toSlashPath } from "./paths";
|
|
4
5
|
import type { ArchConfig, Severity, Violation } from "./types";
|
|
5
6
|
|
|
6
|
-
const toSlash = (p: string) => p.replaceAll("\\", "/");
|
|
7
|
-
|
|
8
7
|
interface AliasEntry {
|
|
9
8
|
/** The prefix to match (pattern with `/*` stripped, e.g. `"@"` from `"@/*"`). */
|
|
10
9
|
prefix: string;
|
|
@@ -31,7 +30,7 @@ function loadPathAliases(root: string): AliasEntry[] {
|
|
|
31
30
|
const prefix = isWildcard ? pattern.slice(0, -2) : pattern;
|
|
32
31
|
const targetStr = targets[0];
|
|
33
32
|
const targetBase = targetStr.endsWith("/*") ? targetStr.slice(0, -2) : targetStr;
|
|
34
|
-
entries.push({ prefix, base:
|
|
33
|
+
entries.push({ prefix, base: toSlashPath(resolve(base, targetBase)), isWildcard });
|
|
35
34
|
}
|
|
36
35
|
return entries;
|
|
37
36
|
}
|
|
@@ -41,7 +40,7 @@ function resolveAlias(importPath: string, aliases: AliasEntry[]): string | undef
|
|
|
41
40
|
if (alias.isWildcard) {
|
|
42
41
|
const matchPrefix = `${alias.prefix}/`;
|
|
43
42
|
if (importPath.startsWith(matchPrefix)) {
|
|
44
|
-
return
|
|
43
|
+
return toSlashPath(resolve(alias.base, importPath.slice(matchPrefix.length)));
|
|
45
44
|
}
|
|
46
45
|
} else if (importPath === alias.prefix) {
|
|
47
46
|
return alias.base;
|
|
@@ -62,7 +61,7 @@ function isBareSpecifier(path: string): boolean {
|
|
|
62
61
|
}
|
|
63
62
|
|
|
64
63
|
function globToPrefix(glob: string, root: string): string {
|
|
65
|
-
return
|
|
64
|
+
return toSlashPath(resolve(root, glob.split("/**")[0].split("/*")[0].split("*")[0]));
|
|
66
65
|
}
|
|
67
66
|
|
|
68
67
|
function resolveGlobs(
|
|
@@ -145,7 +144,7 @@ function scanImports(
|
|
|
145
144
|
if (isBareSpecifier(path)) {
|
|
146
145
|
resolved = resolveAlias(path, aliases) ?? path;
|
|
147
146
|
} else {
|
|
148
|
-
resolved =
|
|
147
|
+
resolved = toSlashPath(resolve(fileDir, path));
|
|
149
148
|
}
|
|
150
149
|
const { line } = src.getLineAndCharacterOfPosition(specifierNode.getStart());
|
|
151
150
|
records.push({ path, resolved, isTypeOnly, line: line + 1 });
|
|
@@ -176,7 +175,7 @@ export function runArchRules(
|
|
|
176
175
|
if (isBareSpecifier(imp.resolved)) continue;
|
|
177
176
|
if (rule["type-only"] === "allow" && imp.isTypeOnly) continue;
|
|
178
177
|
|
|
179
|
-
const relFile =
|
|
178
|
+
const relFile = relativeSlashPath(root, file);
|
|
180
179
|
|
|
181
180
|
if (rule.deny !== undefined) {
|
|
182
181
|
const denyPrefixes = resolveLayerPrefixes(rule.deny, layers, root);
|
|
@@ -235,7 +234,7 @@ export function runArchRules(
|
|
|
235
234
|
|
|
236
235
|
for (const rule of arch.singleton ?? []) {
|
|
237
236
|
const severity: Severity = rule.severity ?? "error";
|
|
238
|
-
const onlyFile =
|
|
237
|
+
const onlyFile = toSlashPath(resolve(root, rule.only));
|
|
239
238
|
const inFiles = rule.in
|
|
240
239
|
? resolveLayerFiles(rule.in, layers, root, allFiles)
|
|
241
240
|
: allFiles;
|
|
@@ -294,7 +293,7 @@ function scanJsxElements(
|
|
|
294
293
|
if (!targets.has(tagName.text)) return;
|
|
295
294
|
const { line } = src.getLineAndCharacterOfPosition(tagName.getStart());
|
|
296
295
|
violations.push({
|
|
297
|
-
file:
|
|
296
|
+
file: relativeSlashPath(root, file),
|
|
298
297
|
line: line + 1,
|
|
299
298
|
message,
|
|
300
299
|
rule: ruleName,
|
|
@@ -321,7 +320,7 @@ function scanLinesForPattern(
|
|
|
321
320
|
for (let i = 0; i < lines.length; i++) {
|
|
322
321
|
if (lines[i].includes(pattern)) {
|
|
323
322
|
violations.push({
|
|
324
|
-
file:
|
|
323
|
+
file: relativeSlashPath(root, file),
|
|
325
324
|
line: i + 1,
|
|
326
325
|
message,
|
|
327
326
|
rule: ruleName,
|
package/core/paths.ts
ADDED
package/core/rule-helpers.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { relative } from "node:path";
|
|
2
1
|
import ts from "typescript";
|
|
3
2
|
import { createProgram } from "./ast";
|
|
3
|
+
import { relativeSlashPath } from "./paths";
|
|
4
4
|
import type { KlintRule, RawViolation, RuleMeta } from "./types";
|
|
5
5
|
import { defineRule } from "./types";
|
|
6
6
|
|
|
@@ -34,7 +34,7 @@ export function defineAstRule(opts: {
|
|
|
34
34
|
report(node, message) {
|
|
35
35
|
const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
36
36
|
violations.push({
|
|
37
|
-
file:
|
|
37
|
+
file: relativeSlashPath(root, sourceFile.fileName),
|
|
38
38
|
line: line + 1,
|
|
39
39
|
message,
|
|
40
40
|
});
|
package/core/runner.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { readdirSync, readFileSync } from "node:fs";
|
|
2
|
-
import { join,
|
|
2
|
+
import { join, resolve } from "node:path";
|
|
3
3
|
import { BUILT_IN_PLUGINS } from "../plugins/index";
|
|
4
4
|
import { BUILT_IN_RULES } from "../rules/index";
|
|
5
5
|
import { runArchRules } from "./arch";
|
|
6
6
|
import { clearAstCache } from "./ast";
|
|
7
|
+
import { relativeSlashPath, toSlashPath } from "./paths";
|
|
7
8
|
import type {
|
|
8
9
|
KlintConfig,
|
|
9
10
|
KlintRule,
|
|
@@ -29,7 +30,7 @@ function walk(dir: string, root: string, excludes: string[] = []): string[] {
|
|
|
29
30
|
const out: string[] = [];
|
|
30
31
|
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
31
32
|
const full = join(dir, entry.name);
|
|
32
|
-
const rel =
|
|
33
|
+
const rel = relativeSlashPath(root, full);
|
|
33
34
|
if (entry.isDirectory()) {
|
|
34
35
|
if (
|
|
35
36
|
excludes.some(
|
|
@@ -38,8 +39,7 @@ function walk(dir: string, root: string, excludes: string[] = []): string[] {
|
|
|
38
39
|
)
|
|
39
40
|
continue;
|
|
40
41
|
out.push(...walk(full, root, excludes));
|
|
41
|
-
} else if (/\.(tsx?|jsx?|mts|cts)$/.test(entry.name))
|
|
42
|
-
out.push(full.replaceAll("\\", "/"));
|
|
42
|
+
} else if (/\.(tsx?|jsx?|mts|cts)$/.test(entry.name)) out.push(toSlashPath(full));
|
|
43
43
|
}
|
|
44
44
|
return out;
|
|
45
45
|
}
|
|
@@ -68,8 +68,8 @@ function resolveFiles(
|
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
function matchPattern(relPath: string, pattern: string): boolean {
|
|
71
|
-
const norm = relPath
|
|
72
|
-
const p = pattern
|
|
71
|
+
const norm = toSlashPath(relPath);
|
|
72
|
+
const p = toSlashPath(pattern);
|
|
73
73
|
if (p === "." || p === "**") return true;
|
|
74
74
|
if (!p.includes("*")) return norm === p || norm.startsWith(`${p}/`);
|
|
75
75
|
return globToRegExp(p).test(norm);
|
|
@@ -107,7 +107,7 @@ function applyPatterns(files: string[], patterns: string[], root: string): strin
|
|
|
107
107
|
const includes = patterns.filter((p) => !p.startsWith("!"));
|
|
108
108
|
const excludes = patterns.filter((p) => p.startsWith("!")).map((p) => p.slice(1));
|
|
109
109
|
return files.filter((file) => {
|
|
110
|
-
const rel =
|
|
110
|
+
const rel = relativeSlashPath(root, file);
|
|
111
111
|
const included = includes.length === 0 || includes.some((p) => matchPattern(rel, p));
|
|
112
112
|
const excluded = excludes.some((p) => matchPattern(rel, p));
|
|
113
113
|
return included && !excluded;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@konvert7/klint",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Architecture-as-Code linter for TypeScript. Enforce layer boundaries, import rules, and singleton patterns in YAML.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -62,10 +62,10 @@
|
|
|
62
62
|
"zod": "^4.4.3"
|
|
63
63
|
},
|
|
64
64
|
"optionalDependencies": {
|
|
65
|
-
"@konvert7/klint-darwin-arm64": "0.
|
|
66
|
-
"@konvert7/klint-darwin-x64": "0.
|
|
67
|
-
"@konvert7/klint-linux-x64": "0.
|
|
68
|
-
"@konvert7/klint-win32-x64": "0.
|
|
65
|
+
"@konvert7/klint-darwin-arm64": "0.20.0",
|
|
66
|
+
"@konvert7/klint-darwin-x64": "0.20.0",
|
|
67
|
+
"@konvert7/klint-linux-x64": "0.20.0",
|
|
68
|
+
"@konvert7/klint-win32-x64": "0.20.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@biomejs/biome": "2.4.7",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { relative } from "node:path";
|
|
2
1
|
import ts from "typescript";
|
|
3
2
|
import { walkAst } from "../core/ast";
|
|
3
|
+
import { relativeSlashPath } from "../core/paths";
|
|
4
4
|
import type { KlintRule } from "../core/types";
|
|
5
5
|
|
|
6
6
|
export const noConsecutiveArrayPush: KlintRule = {
|
|
@@ -26,7 +26,7 @@ export const noConsecutiveArrayPush: KlintRule = {
|
|
|
26
26
|
statements[runStart].getStart()
|
|
27
27
|
);
|
|
28
28
|
violations.push({
|
|
29
|
-
file:
|
|
29
|
+
file: relativeSlashPath(root, file),
|
|
30
30
|
line: line + 1,
|
|
31
31
|
message: `${upTo - runStart} consecutive .push() calls on \`${runReceiver}\` — combine into a single .push(a, b, …) call.`,
|
|
32
32
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { relative } from "node:path";
|
|
2
1
|
import ts from "typescript";
|
|
3
2
|
import { walkAst } from "../core/ast";
|
|
3
|
+
import { relativeSlashPath } from "../core/paths";
|
|
4
4
|
import type { KlintRule } from "../core/types";
|
|
5
5
|
|
|
6
6
|
export const noNestedTemplateLiterals: KlintRule = {
|
|
@@ -32,7 +32,7 @@ function findNestedTemplate(
|
|
|
32
32
|
if (ts.isTemplateExpression(node) || ts.isNoSubstitutionTemplateLiteral(node)) {
|
|
33
33
|
const { line } = src.getLineAndCharacterOfPosition(node.getStart());
|
|
34
34
|
violations.push({
|
|
35
|
-
file:
|
|
35
|
+
file: relativeSlashPath(root, file),
|
|
36
36
|
line: line + 1,
|
|
37
37
|
message:
|
|
38
38
|
"Nested template literal — extract the inner template to a variable to improve readability.",
|
|
@@ -87,7 +87,7 @@ function wouldRenderAsObjectObject(type: ts.Type, checker: ts.TypeChecker): bool
|
|
|
87
87
|
const fileName = d.getSourceFile().fileName;
|
|
88
88
|
return (
|
|
89
89
|
!fileName.includes("/typescript/lib/lib") &&
|
|
90
|
-
!fileName.includes(
|
|
90
|
+
!fileName.includes(String.raw`\typescript\lib\lib`)
|
|
91
91
|
);
|
|
92
92
|
});
|
|
93
93
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { relative } from "node:path";
|
|
2
1
|
import ts from "typescript";
|
|
3
2
|
import { walkAst } from "../core/ast";
|
|
3
|
+
import { relativeSlashPath } from "../core/paths";
|
|
4
4
|
import { buildNodeReplacementFix } from "../core/rule-helpers";
|
|
5
5
|
import type { KlintRule } from "../core/types";
|
|
6
6
|
|
|
@@ -99,7 +99,7 @@ export const noSingleCharClass: KlintRule = {
|
|
|
99
99
|
const fix = buildNodeReplacementFix(src, node, fixedRegex);
|
|
100
100
|
|
|
101
101
|
violations.push({
|
|
102
|
-
file:
|
|
102
|
+
file: relativeSlashPath(root, file),
|
|
103
103
|
line: fix.startLine,
|
|
104
104
|
message: `Character class [${toFix[0].inner}] contains a single element — remove the brackets.`,
|
|
105
105
|
fix,
|
package/rules/no-string-match.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { relative } from "node:path";
|
|
2
1
|
import ts from "typescript";
|
|
3
2
|
import { walkAst } from "../core/ast";
|
|
3
|
+
import { relativeSlashPath } from "../core/paths";
|
|
4
4
|
import type { KlintRule } from "../core/types";
|
|
5
5
|
|
|
6
6
|
export const noStringMatch: KlintRule = {
|
|
@@ -45,7 +45,7 @@ export const noStringMatch: KlintRule = {
|
|
|
45
45
|
})()
|
|
46
46
|
: undefined;
|
|
47
47
|
violations.push({
|
|
48
|
-
file:
|
|
48
|
+
file: relativeSlashPath(root, file),
|
|
49
49
|
line: s + 1,
|
|
50
50
|
message: `Use RegExp.exec() instead of String.match() for non-global regexes — use new RegExp(${regexText}).exec(${strText}) instead.`,
|
|
51
51
|
fix,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { relative } from "node:path";
|
|
2
1
|
import ts from "typescript";
|
|
3
2
|
import { nearestFunctionIsAsync, walkAst } from "../core/ast";
|
|
3
|
+
import { relativeSlashPath } from "../core/paths";
|
|
4
4
|
import type { KlintRule } from "../core/types";
|
|
5
5
|
|
|
6
6
|
export const noSyncInAsync: KlintRule = {
|
|
@@ -31,7 +31,7 @@ export const noSyncInAsync: KlintRule = {
|
|
|
31
31
|
) {
|
|
32
32
|
const { line } = src.getLineAndCharacterOfPosition(node.getStart());
|
|
33
33
|
violations.push({
|
|
34
|
-
file:
|
|
34
|
+
file: relativeSlashPath(root, file),
|
|
35
35
|
line: line + 1,
|
|
36
36
|
message: `${name}() blocks the event loop inside an async function — use the async equivalent from node:fs/promises.`,
|
|
37
37
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { relative } from "node:path";
|
|
2
1
|
import ts from "typescript";
|
|
3
2
|
import { isInsideTry, walkAst } from "../core/ast";
|
|
3
|
+
import { relativeSlashPath } from "../core/paths";
|
|
4
4
|
import type { KlintRule } from "../core/types";
|
|
5
5
|
|
|
6
6
|
export const noUnguardedJsonParse: KlintRule = {
|
|
@@ -23,7 +23,7 @@ export const noUnguardedJsonParse: KlintRule = {
|
|
|
23
23
|
) {
|
|
24
24
|
const { line } = src.getLineAndCharacterOfPosition(node.getStart());
|
|
25
25
|
violations.push({
|
|
26
|
-
file:
|
|
26
|
+
file: relativeSlashPath(root, file),
|
|
27
27
|
line: line + 1,
|
|
28
28
|
message:
|
|
29
29
|
"JSON.parse() called without a surrounding try/catch — a malformed payload will throw an unhandled exception.",
|
package/rules/prefer-at.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { relative } from "node:path";
|
|
2
1
|
import ts from "typescript";
|
|
3
2
|
import { walkAst } from "../core/ast";
|
|
3
|
+
import { relativeSlashPath } from "../core/paths";
|
|
4
4
|
import { buildNodeReplacementFix } from "../core/rule-helpers";
|
|
5
5
|
import type { KlintRule } from "../core/types";
|
|
6
6
|
|
|
@@ -35,7 +35,7 @@ export const preferAt: KlintRule = {
|
|
|
35
35
|
const fix = buildNodeReplacementFix(src, node, fixedCall);
|
|
36
36
|
|
|
37
37
|
violations.push({
|
|
38
|
-
file:
|
|
38
|
+
file: relativeSlashPath(root, file),
|
|
39
39
|
line: fix.startLine,
|
|
40
40
|
message: `Prefer ${baseText}.at(-${n}) over ${baseText}[${baseText}.length - ${n}] for cleaner negative indexing.`,
|
|
41
41
|
fix,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { relative } from "node:path";
|
|
2
1
|
import ts from "typescript";
|
|
3
2
|
import { walkAst } from "../core/ast";
|
|
3
|
+
import { relativeSlashPath } from "../core/paths";
|
|
4
4
|
import type { KlintRule } from "../core/types";
|
|
5
5
|
|
|
6
6
|
export const preferNullishCoalescingAssign: KlintRule = {
|
|
@@ -33,7 +33,7 @@ export const preferNullishCoalescingAssign: KlintRule = {
|
|
|
33
33
|
const indent = getIndent(src, node);
|
|
34
34
|
|
|
35
35
|
violations.push({
|
|
36
|
-
file:
|
|
36
|
+
file: relativeSlashPath(root, file),
|
|
37
37
|
line: s + 1,
|
|
38
38
|
message: `Prefer \`${xText} ??= ${yText}\` over \`if (!${xText}) ${xText} = ${yText}\` — ??= only assigns when null or undefined.`,
|
|
39
39
|
fix: {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { relative } from "node:path";
|
|
2
1
|
import ts from "typescript";
|
|
3
2
|
import { walkAst } from "../core/ast";
|
|
3
|
+
import { relativeSlashPath } from "../core/paths";
|
|
4
4
|
import type { KlintRule } from "../core/types";
|
|
5
5
|
|
|
6
6
|
export const preferStringRawRegexp: KlintRule = {
|
|
@@ -47,7 +47,7 @@ export const preferStringRawRegexp: KlintRule = {
|
|
|
47
47
|
linesText.slice(0, argOffset) + fixedArg + linesText.slice(argEndOffset);
|
|
48
48
|
|
|
49
49
|
violations.push({
|
|
50
|
-
file:
|
|
50
|
+
file: relativeSlashPath(root, file),
|
|
51
51
|
line: line + 1,
|
|
52
52
|
message:
|
|
53
53
|
"Use String.raw`...` for RegExp template argument to avoid double backslashes (Sonar S7780).",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { relative } from "node:path";
|
|
2
1
|
import ts from "typescript";
|
|
3
2
|
import { walkAst } from "../core/ast";
|
|
3
|
+
import { relativeSlashPath } from "../core/paths";
|
|
4
4
|
import { buildNodeReplacementFix } from "../core/rule-helpers";
|
|
5
5
|
import type { KlintRule } from "../core/types";
|
|
6
6
|
|
|
@@ -27,7 +27,7 @@ export const preferStringRaw: KlintRule = {
|
|
|
27
27
|
const fix = buildNodeReplacementFix(src, node, fixedNode);
|
|
28
28
|
|
|
29
29
|
violations.push({
|
|
30
|
-
file:
|
|
30
|
+
file: relativeSlashPath(root, file),
|
|
31
31
|
line: fix.startLine,
|
|
32
32
|
message:
|
|
33
33
|
"String literal with escaped backslashes — use String.raw`...` for clarity (Sonar S6535).",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { relative } from "node:path";
|
|
2
1
|
import ts from "typescript";
|
|
3
2
|
import { walkAst } from "../core/ast";
|
|
3
|
+
import { relativeSlashPath } from "../core/paths";
|
|
4
4
|
import { buildNodeReplacementFix } from "../core/rule-helpers";
|
|
5
5
|
import type { KlintRule } from "../core/types";
|
|
6
6
|
|
|
@@ -35,13 +35,13 @@ export const preferStringReplaceall: KlintRule = {
|
|
|
35
35
|
).expression.getText(src);
|
|
36
36
|
const replacementText = node.arguments[1].getText(src);
|
|
37
37
|
const patternLit = pattern.includes('"')
|
|
38
|
-
? `'${pattern.replaceAll("'",
|
|
38
|
+
? `'${pattern.replaceAll("'", String.raw`\'`)}'`
|
|
39
39
|
: `"${pattern}"`;
|
|
40
40
|
const fixedCall = `${strText}.replaceAll(${patternLit}, ${replacementText})`;
|
|
41
41
|
const fix = buildNodeReplacementFix(src, node, fixedCall);
|
|
42
42
|
|
|
43
43
|
violations.push({
|
|
44
|
-
file:
|
|
44
|
+
file: relativeSlashPath(root, file),
|
|
45
45
|
line: fix.startLine,
|
|
46
46
|
message: `Prefer \`${strText}.replaceAll(${patternLit}, ...)\` over \`.replace(/${pattern}/g, ...)\` — replaceAll() with a string is clearer and avoids regex escaping pitfalls.`,
|
|
47
47
|
fix,
|
|
@@ -28,22 +28,31 @@ export async function prepare(_, context) {
|
|
|
28
28
|
|
|
29
29
|
const packageJson = readPackageJson(cwd);
|
|
30
30
|
packageJson.version = version;
|
|
31
|
-
packageJson.optionalDependencies ??= {};
|
|
32
|
-
for (const packageName of NATIVE_PACKAGE_NAMES) {
|
|
33
|
-
packageJson.optionalDependencies[packageName] = version;
|
|
34
|
-
}
|
|
35
31
|
writePackageJson(cwd, packageJson);
|
|
36
32
|
}
|
|
37
33
|
|
|
38
|
-
export async function publish(
|
|
34
|
+
export async function publish(pluginConfig = {}, context) {
|
|
39
35
|
const cwd = context.cwd ?? process.cwd();
|
|
36
|
+
const spawn = pluginConfig.spawnSync ?? spawnSync;
|
|
37
|
+
const version = context.nextRelease?.version;
|
|
40
38
|
|
|
41
39
|
if (context.options?.dryRun) {
|
|
42
40
|
context.logger?.log("Dry run: skipping npm publish");
|
|
43
41
|
return;
|
|
44
42
|
}
|
|
45
43
|
|
|
46
|
-
|
|
44
|
+
if (!version) {
|
|
45
|
+
throw new Error("semantic-release did not provide nextRelease.version");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const packageJson = readPackageJson(cwd);
|
|
49
|
+
packageJson.optionalDependencies ??= {};
|
|
50
|
+
for (const packageName of NATIVE_PACKAGE_NAMES) {
|
|
51
|
+
packageJson.optionalDependencies[packageName] = version;
|
|
52
|
+
}
|
|
53
|
+
writePackageJson(cwd, packageJson);
|
|
54
|
+
|
|
55
|
+
const result = spawn("npm", ["publish", "--access", "public"], {
|
|
47
56
|
cwd,
|
|
48
57
|
encoding: "utf-8",
|
|
49
58
|
stdio: "inherit",
|