@cldmv/fix-headers 1.0.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 +157 -0
- package/index.cjs +39 -0
- package/index.mjs +7 -0
- package/package.json +56 -0
- package/src/cli.mjs +216 -0
- package/src/constants.mjs +15 -0
- package/src/core/file-discovery.mjs +133 -0
- package/src/core/fix-headers.mjs +210 -0
- package/src/detect/project.mjs +175 -0
- package/src/detectors/css.mjs +54 -0
- package/src/detectors/go.mjs +52 -0
- package/src/detectors/html.mjs +54 -0
- package/src/detectors/index.mjs +160 -0
- package/src/detectors/node.mjs +63 -0
- package/src/detectors/php.mjs +53 -0
- package/src/detectors/python.mjs +48 -0
- package/src/detectors/rust.mjs +49 -0
- package/src/detectors/shared.mjs +32 -0
- package/src/fix-header.mjs +30 -0
- package/src/header/parser.mjs +76 -0
- package/src/header/syntax.mjs +54 -0
- package/src/header/template.mjs +44 -0
- package/src/utils/fs.mjs +125 -0
- package/src/utils/git.mjs +97 -0
- package/src/utils/time.mjs +31 -0
- package/types/index.d.mts +2 -0
- package/types/src/cli.d.mts +44 -0
- package/types/src/constants.d.mts +7 -0
- package/types/src/core/file-discovery.d.mts +22 -0
- package/types/src/core/fix-headers.d.mts +86 -0
- package/types/src/detect/project.d.mts +81 -0
- package/types/src/detectors/css.d.mts +25 -0
- package/types/src/detectors/go.d.mts +25 -0
- package/types/src/detectors/html.d.mts +25 -0
- package/types/src/detectors/index.d.mts +66 -0
- package/types/src/detectors/node.d.mts +25 -0
- package/types/src/detectors/php.d.mts +25 -0
- package/types/src/detectors/python.d.mts +23 -0
- package/types/src/detectors/rust.d.mts +25 -0
- package/types/src/detectors/shared.d.mts +14 -0
- package/types/src/fix-header.d.mts +12 -0
- package/types/src/header/parser.d.mts +43 -0
- package/types/src/header/syntax.d.mts +44 -0
- package/types/src/header/template.d.mts +52 -0
- package/types/src/utils/fs.d.mts +50 -0
- package/types/src/utils/git.d.mts +40 -0
- package/types/src/utils/time.d.mts +19 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { readFile, stat, writeFile } from "node:fs/promises";
|
|
2
|
+
import { relative, resolve } from "node:path";
|
|
3
|
+
import { discoverFiles } from "./file-discovery.mjs";
|
|
4
|
+
import { resolveProjectMetadata } from "../detect/project.mjs";
|
|
5
|
+
import { buildHeader } from "../header/template.mjs";
|
|
6
|
+
import { replaceOrInsertHeader } from "../header/parser.mjs";
|
|
7
|
+
import { readFileDates } from "../utils/fs.mjs";
|
|
8
|
+
import { getGitCreationDate, getGitLastModifiedDate } from "../utils/git.mjs";
|
|
9
|
+
import { toDatePayload } from "../utils/time.mjs";
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @typedef {{
|
|
13
|
+
* cwd?: string,
|
|
14
|
+
* input?: string,
|
|
15
|
+
* dryRun?: boolean,
|
|
16
|
+
* configFile?: string,
|
|
17
|
+
* enabledDetectors?: string[],
|
|
18
|
+
* disabledDetectors?: string[],
|
|
19
|
+
* detectorSyntaxOverrides?: Record<string, { linePrefix?: string, blockStart?: string, blockLinePrefix?: string, blockEnd?: string }>,
|
|
20
|
+
* includeFolders?: string[],
|
|
21
|
+
* excludeFolders?: string[],
|
|
22
|
+
* includeExtensions?: string[],
|
|
23
|
+
* projectName?: string,
|
|
24
|
+
* language?: string,
|
|
25
|
+
* projectRoot?: string,
|
|
26
|
+
* marker?: string | null,
|
|
27
|
+
* authorName?: string,
|
|
28
|
+
* authorEmail?: string,
|
|
29
|
+
* companyName?: string,
|
|
30
|
+
* copyrightStartYear?: number
|
|
31
|
+
* }} FixHeadersOptions
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* @typedef {{
|
|
36
|
+
* metadata: {
|
|
37
|
+
* projectName: string,
|
|
38
|
+
* language: string,
|
|
39
|
+
* projectRoot: string,
|
|
40
|
+
* marker: string | null,
|
|
41
|
+
* authorName: string,
|
|
42
|
+
* authorEmail: string,
|
|
43
|
+
* companyName: string,
|
|
44
|
+
* copyrightStartYear: number
|
|
45
|
+
* },
|
|
46
|
+
* detectedProjects: string[],
|
|
47
|
+
* filesScanned: number,
|
|
48
|
+
* filesUpdated: number,
|
|
49
|
+
* dryRun: boolean,
|
|
50
|
+
* changes: Array<{file: string, changed: boolean}>
|
|
51
|
+
* }} FixHeadersResult
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Resolves runtime options including optional JSON config file loading.
|
|
56
|
+
* @param {FixHeadersOptions} options - Runtime options.
|
|
57
|
+
* @returns {Promise<FixHeadersOptions>} Effective runtime options.
|
|
58
|
+
*/
|
|
59
|
+
async function resolveRuntimeOptions(options) {
|
|
60
|
+
const configFile = typeof options.configFile === "string" && options.configFile.trim().length > 0 ? options.configFile.trim() : null;
|
|
61
|
+
|
|
62
|
+
if (!configFile) {
|
|
63
|
+
return options;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const baseCwd = typeof options.cwd === "string" && options.cwd.length > 0 ? options.cwd : process.cwd();
|
|
67
|
+
const absoluteConfigPath = resolve(baseCwd, configFile);
|
|
68
|
+
const raw = await readFile(absoluteConfigPath, "utf8");
|
|
69
|
+
const parsed = JSON.parse(raw);
|
|
70
|
+
|
|
71
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
72
|
+
throw new Error(`Config file must contain a JSON object: ${configFile}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const merged = {
|
|
76
|
+
...parsed,
|
|
77
|
+
...options
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
delete merged.configFile;
|
|
81
|
+
/** @type {FixHeadersOptions} */
|
|
82
|
+
const output = merged;
|
|
83
|
+
return output;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* @fileoverview Main header-fixing engine with auto-detection and override support.
|
|
88
|
+
* @module fix-headers/core/fix-headers
|
|
89
|
+
*/
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Fixes headers in a project using auto-detected metadata unless overridden.
|
|
93
|
+
* @param {FixHeadersOptions} [options={}] - Runtime options.
|
|
94
|
+
* @returns {Promise<FixHeadersResult>} Process report.
|
|
95
|
+
*/
|
|
96
|
+
export async function fixHeaders(options = {}) {
|
|
97
|
+
const effectiveOptions = await resolveRuntimeOptions(options);
|
|
98
|
+
const scanRoot = resolve(effectiveOptions.projectRoot || effectiveOptions.cwd || process.cwd());
|
|
99
|
+
const metadata = await resolveProjectMetadata({
|
|
100
|
+
...effectiveOptions,
|
|
101
|
+
cwd: scanRoot
|
|
102
|
+
});
|
|
103
|
+
const dryRun = effectiveOptions.dryRun === true;
|
|
104
|
+
|
|
105
|
+
let files = [];
|
|
106
|
+
if (typeof effectiveOptions.input === "string" && effectiveOptions.input.trim().length > 0) {
|
|
107
|
+
const inputPath = resolve(scanRoot, effectiveOptions.input);
|
|
108
|
+
const targetStats = await stat(inputPath).catch(() => null);
|
|
109
|
+
if (!targetStats) {
|
|
110
|
+
throw new Error(`Input path does not exist: ${effectiveOptions.input}`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (targetStats.isFile()) {
|
|
114
|
+
files = [inputPath];
|
|
115
|
+
} else if (targetStats.isDirectory()) {
|
|
116
|
+
files = await discoverFiles({
|
|
117
|
+
projectRoot: inputPath,
|
|
118
|
+
language: metadata.language,
|
|
119
|
+
enabledDetectors: effectiveOptions.enabledDetectors,
|
|
120
|
+
disabledDetectors: effectiveOptions.disabledDetectors,
|
|
121
|
+
includeFolders: effectiveOptions.includeFolders,
|
|
122
|
+
excludeFolders: effectiveOptions.excludeFolders,
|
|
123
|
+
includeExtensions: effectiveOptions.includeExtensions
|
|
124
|
+
});
|
|
125
|
+
} else {
|
|
126
|
+
throw new Error(`Input path must be a file or directory: ${effectiveOptions.input}`);
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
files = await discoverFiles({
|
|
130
|
+
projectRoot: scanRoot,
|
|
131
|
+
language: metadata.language,
|
|
132
|
+
enabledDetectors: effectiveOptions.enabledDetectors,
|
|
133
|
+
disabledDetectors: effectiveOptions.disabledDetectors,
|
|
134
|
+
includeFolders: effectiveOptions.includeFolders,
|
|
135
|
+
excludeFolders: effectiveOptions.excludeFolders,
|
|
136
|
+
includeExtensions: effectiveOptions.includeExtensions
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const currentYear = new Date().getFullYear();
|
|
141
|
+
const changes = [];
|
|
142
|
+
const detectedProjects = new Set();
|
|
143
|
+
let filesUpdated = 0;
|
|
144
|
+
|
|
145
|
+
for (const filePath of files) {
|
|
146
|
+
const fileMetadata = await resolveProjectMetadata({
|
|
147
|
+
...effectiveOptions,
|
|
148
|
+
cwd: scanRoot,
|
|
149
|
+
targetFilePath: filePath
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
detectedProjects.add(`${fileMetadata.language}:${fileMetadata.projectRoot}`);
|
|
153
|
+
const relativePath = relative(scanRoot, filePath);
|
|
154
|
+
const original = await readFile(filePath, "utf8");
|
|
155
|
+
const filesystemDates = await readFileDates(filePath);
|
|
156
|
+
|
|
157
|
+
const metadataRelativePath = relative(fileMetadata.projectRoot, filePath);
|
|
158
|
+
const gitCreated = await getGitCreationDate(fileMetadata.projectRoot, metadataRelativePath);
|
|
159
|
+
const gitLastUpdated = await getGitLastModifiedDate(fileMetadata.projectRoot, metadataRelativePath);
|
|
160
|
+
|
|
161
|
+
const createdAt = gitCreated || toDatePayload(filesystemDates.createdAt);
|
|
162
|
+
const lastModifiedAt = gitLastUpdated || toDatePayload(filesystemDates.updatedAt);
|
|
163
|
+
|
|
164
|
+
const header = buildHeader({
|
|
165
|
+
absoluteFilePath: filePath,
|
|
166
|
+
language: fileMetadata.language,
|
|
167
|
+
syntaxOptions: {
|
|
168
|
+
language: fileMetadata.language,
|
|
169
|
+
enabledDetectors: effectiveOptions.enabledDetectors,
|
|
170
|
+
disabledDetectors: effectiveOptions.disabledDetectors,
|
|
171
|
+
detectorSyntaxOverrides: effectiveOptions.detectorSyntaxOverrides
|
|
172
|
+
},
|
|
173
|
+
projectRoot: fileMetadata.projectRoot,
|
|
174
|
+
projectName: fileMetadata.projectName,
|
|
175
|
+
authorName: fileMetadata.authorName,
|
|
176
|
+
authorEmail: fileMetadata.authorEmail,
|
|
177
|
+
createdAt,
|
|
178
|
+
lastModifiedAt,
|
|
179
|
+
copyrightStartYear: fileMetadata.copyrightStartYear,
|
|
180
|
+
companyName: fileMetadata.companyName,
|
|
181
|
+
currentYear
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const replacement = replaceOrInsertHeader(original, header, filePath, {
|
|
185
|
+
language: fileMetadata.language,
|
|
186
|
+
enabledDetectors: effectiveOptions.enabledDetectors,
|
|
187
|
+
disabledDetectors: effectiveOptions.disabledDetectors,
|
|
188
|
+
detectorSyntaxOverrides: effectiveOptions.detectorSyntaxOverrides
|
|
189
|
+
});
|
|
190
|
+
changes.push({ file: relativePath, changed: replacement.changed });
|
|
191
|
+
|
|
192
|
+
if (!replacement.changed) {
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
filesUpdated += 1;
|
|
197
|
+
if (!dryRun) {
|
|
198
|
+
await writeFile(filePath, replacement.nextContent, "utf8");
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
metadata,
|
|
204
|
+
detectedProjects: Array.from(detectedProjects),
|
|
205
|
+
filesScanned: files.length,
|
|
206
|
+
filesUpdated,
|
|
207
|
+
dryRun,
|
|
208
|
+
changes
|
|
209
|
+
};
|
|
210
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @Project: @cldmv/fix-headers
|
|
3
|
+
* @Filename: /src/detect/project.mjs
|
|
4
|
+
* @Date: 2026-03-01 13:32:57 -08:00 (1772400777)
|
|
5
|
+
* @Author: Nate Hyson <CLDMV>
|
|
6
|
+
* @Email: <Shinrai@users.noreply.github.com>
|
|
7
|
+
* -----
|
|
8
|
+
* @Last modified by: Nate Hyson <CLDMV> (Shinrai@users.noreply.github.com)
|
|
9
|
+
* @Last modified time: 2026-03-01 16:29:34 -08:00 (1772411374)
|
|
10
|
+
* -----
|
|
11
|
+
* @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @Project: @cldmv/fix-headers
|
|
16
|
+
* @Filename: /src/detect/project.mjs
|
|
17
|
+
* @Date: 2026-03-01 13:32:57 -08:00 (1772400777)
|
|
18
|
+
* @Author: Nate Hyson <CLDMV>
|
|
19
|
+
* @Email: <Shinrai@users.noreply.github.com>
|
|
20
|
+
* -----
|
|
21
|
+
* @Last modified by: Nate Hyson <CLDMV> (Shinrai@users.noreply.github.com)
|
|
22
|
+
* @Last modified time: 2026-03-01 16:27:47 -08:00 (1772411267)
|
|
23
|
+
* -----
|
|
24
|
+
* @Copyright: Copyright (c) 2013-2026 Catalyzed Motivation Inc. All rights reserved.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { dirname, extname, join, resolve } from "node:path";
|
|
28
|
+
import { DEFAULT_COMPANY_NAME } from "../constants.mjs";
|
|
29
|
+
import { getEnabledDetectors } from "../detectors/index.mjs";
|
|
30
|
+
import { readTextIfExists } from "../utils/fs.mjs";
|
|
31
|
+
import { detectGitAuthor } from "../utils/git.mjs";
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @fileoverview Auto-detects project metadata from known language markers and git config.
|
|
35
|
+
* @module fix-headers/detect/project
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
async function findClosestDetectorMatch(startPath, detectors, preferredExtension = "") {
|
|
39
|
+
const extension = typeof preferredExtension === "string" ? preferredExtension.trim().toLowerCase() : "";
|
|
40
|
+
const extensionMatched =
|
|
41
|
+
extension.length > 0
|
|
42
|
+
? detectors.filter((detector) => Array.isArray(detector.extensions) && detector.extensions.includes(extension))
|
|
43
|
+
: [];
|
|
44
|
+
const detectorsToSearch = extensionMatched.length > 0 ? extensionMatched : detectors;
|
|
45
|
+
|
|
46
|
+
const matches = await Promise.all(
|
|
47
|
+
detectorsToSearch.map(async (detector) => {
|
|
48
|
+
if (typeof detector.findNearestConfig !== "function") {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const located = await detector.findNearestConfig(startPath);
|
|
53
|
+
if (!located) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
detector,
|
|
59
|
+
root: located.root,
|
|
60
|
+
marker: located.marker
|
|
61
|
+
};
|
|
62
|
+
})
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
const validMatches = matches.filter((item) => item !== null);
|
|
66
|
+
if (validMatches.length === 0) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
let closest = validMatches[0];
|
|
71
|
+
for (const candidate of validMatches.slice(1)) {
|
|
72
|
+
if (candidate.root.length > closest.root.length) {
|
|
73
|
+
closest = candidate;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (candidate.root.length === closest.root.length) {
|
|
78
|
+
const candidatePriority = Number.isFinite(candidate.detector.priority) ? candidate.detector.priority : 0;
|
|
79
|
+
const closestPriority = Number.isFinite(closest.detector.priority) ? closest.detector.priority : 0;
|
|
80
|
+
if (candidatePriority > closestPriority) {
|
|
81
|
+
closest = candidate;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return closest;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Detects project root and language by scanning known marker files.
|
|
91
|
+
* @param {string} cwd - Starting working directory.
|
|
92
|
+
* @param {{ detectors?: { id: string, extensions: string[], priority?: number, findNearestConfig: (path: string) => Promise<{ root: string, marker: string } | null>, parseProjectName: (marker: string, content: string, rootDirName: string) => string }[], enabledDetectors?: string[], disabledDetectors?: string[], preferredExtension?: string }} [options={}] - Detection options.
|
|
93
|
+
* @returns {Promise<{
|
|
94
|
+
* language: string,
|
|
95
|
+
* rootDir: string,
|
|
96
|
+
* marker: string | null,
|
|
97
|
+
* projectName: string
|
|
98
|
+
* }>} Detection result.
|
|
99
|
+
*/
|
|
100
|
+
export async function detectProjectFromMarkers(cwd, options = {}) {
|
|
101
|
+
const detectors = Array.isArray(options.detectors) ? options.detectors : getEnabledDetectors(options);
|
|
102
|
+
const located = await findClosestDetectorMatch(cwd, detectors, options.preferredExtension);
|
|
103
|
+
|
|
104
|
+
if (located) {
|
|
105
|
+
const markerPath = join(located.root, located.marker);
|
|
106
|
+
const markerContent = (await readTextIfExists(markerPath)) || "";
|
|
107
|
+
const rootDirName = located.root.split("/").filter(Boolean).at(-1) || "project";
|
|
108
|
+
const projectName = located.detector.parseProjectName(located.marker, markerContent, rootDirName);
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
language: located.detector.id,
|
|
112
|
+
rootDir: located.root,
|
|
113
|
+
marker: located.marker,
|
|
114
|
+
projectName
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const fallbackRoot = resolve(cwd);
|
|
119
|
+
const fallbackName = fallbackRoot.split("/").filter(Boolean).at(-1) || "project";
|
|
120
|
+
return {
|
|
121
|
+
language: "unknown",
|
|
122
|
+
rootDir: fallbackRoot,
|
|
123
|
+
marker: null,
|
|
124
|
+
projectName: fallbackName
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Resolves project metadata with override support for every auto-detected field.
|
|
130
|
+
* @param {{
|
|
131
|
+
* cwd?: string,
|
|
132
|
+
* targetFilePath?: string,
|
|
133
|
+
* enabledDetectors?: string[],
|
|
134
|
+
* disabledDetectors?: string[],
|
|
135
|
+
* projectName?: string,
|
|
136
|
+
* language?: string,
|
|
137
|
+
* projectRoot?: string,
|
|
138
|
+
* marker?: string | null,
|
|
139
|
+
* authorName?: string,
|
|
140
|
+
* authorEmail?: string,
|
|
141
|
+
* companyName?: string,
|
|
142
|
+
* copyrightStartYear?: number
|
|
143
|
+
* }} [options={}] - Detection options and overrides.
|
|
144
|
+
* @returns {Promise<{
|
|
145
|
+
* projectName: string,
|
|
146
|
+
* language: string,
|
|
147
|
+
* projectRoot: string,
|
|
148
|
+
* marker: string | null,
|
|
149
|
+
* authorName: string,
|
|
150
|
+
* authorEmail: string,
|
|
151
|
+
* companyName: string,
|
|
152
|
+
* copyrightStartYear: number
|
|
153
|
+
* }>} Final metadata.
|
|
154
|
+
*/
|
|
155
|
+
export async function resolveProjectMetadata(options = {}) {
|
|
156
|
+
const basePath = options.targetFilePath || options.cwd || process.cwd();
|
|
157
|
+
const cwd = resolve(basePath);
|
|
158
|
+
const detectors = getEnabledDetectors(options);
|
|
159
|
+
const detectFrom = options.targetFilePath ? dirname(cwd) : cwd;
|
|
160
|
+
const preferredExtension = options.targetFilePath ? extname(cwd).toLowerCase() : "";
|
|
161
|
+
const detected = await detectProjectFromMarkers(detectFrom, { detectors, preferredExtension });
|
|
162
|
+
const gitAuthor = await detectGitAuthor(detected.rootDir);
|
|
163
|
+
const currentYear = new Date().getFullYear();
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
projectName: options.projectName || detected.projectName,
|
|
167
|
+
language: options.language || detected.language,
|
|
168
|
+
projectRoot: options.projectRoot || detected.rootDir,
|
|
169
|
+
marker: options.marker === undefined ? detected.marker : options.marker,
|
|
170
|
+
authorName: options.authorName || gitAuthor.authorName || "Unknown Author",
|
|
171
|
+
authorEmail: options.authorEmail || gitAuthor.authorEmail || "unknown@example.com",
|
|
172
|
+
companyName: options.companyName || DEFAULT_COMPANY_NAME,
|
|
173
|
+
copyrightStartYear: Number.isInteger(options.copyrightStartYear) ? Number(options.copyrightStartYear) : currentYear
|
|
174
|
+
};
|
|
175
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { extname } from "node:path";
|
|
2
|
+
import { findNearestMarker } from "./shared.mjs";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @fileoverview CSS detector implementation.
|
|
6
|
+
* @module fix-headers/detectors/css
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const markers = ["package.json", "postcss.config.js", "postcss.config.cjs", "postcss.config.mjs"];
|
|
10
|
+
const extensions = [".css"];
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Parses CSS project name from marker context.
|
|
14
|
+
* @param {string} rootDirName - Fallback directory name.
|
|
15
|
+
* @returns {string} Project name.
|
|
16
|
+
*/
|
|
17
|
+
function parseCssProjectName(rootDirName) {
|
|
18
|
+
return rootDirName;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Resolves block comment syntax for CSS files.
|
|
23
|
+
* @param {string} filePath - File path.
|
|
24
|
+
* @returns {{kind: "block", blockStart: string, blockLinePrefix: string, blockEnd: string} | null} Syntax descriptor.
|
|
25
|
+
*/
|
|
26
|
+
function resolveCssCommentSyntax(filePath) {
|
|
27
|
+
const extension = extname(filePath).toLowerCase();
|
|
28
|
+
if (extensions.includes(extension)) {
|
|
29
|
+
return {
|
|
30
|
+
kind: "block",
|
|
31
|
+
blockStart: "/*",
|
|
32
|
+
blockLinePrefix: " *\t",
|
|
33
|
+
blockEnd: " */"
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const detector = {
|
|
40
|
+
id: "css",
|
|
41
|
+
priority: 70,
|
|
42
|
+
markers,
|
|
43
|
+
extensions,
|
|
44
|
+
enabledByDefault: true,
|
|
45
|
+
findNearestConfig(startPath) {
|
|
46
|
+
return findNearestMarker(startPath, markers);
|
|
47
|
+
},
|
|
48
|
+
parseProjectName(_marker, _markerContent, rootDirName) {
|
|
49
|
+
return parseCssProjectName(rootDirName);
|
|
50
|
+
},
|
|
51
|
+
resolveCommentSyntax(filePath) {
|
|
52
|
+
return resolveCssCommentSyntax(filePath);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { extname } from "node:path";
|
|
2
|
+
import { findNearestMarker } from "./shared.mjs";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @fileoverview Go detector implementation.
|
|
6
|
+
* @module fix-headers/detectors/go
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const markers = ["go.mod"];
|
|
10
|
+
const extensions = [".go"];
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Resolves Go comment syntax.
|
|
14
|
+
* @param {string} filePath - File path.
|
|
15
|
+
* @returns {{kind: "block", blockStart: string, blockLinePrefix: string, blockEnd: string} | null} Syntax descriptor.
|
|
16
|
+
*/
|
|
17
|
+
function resolveGoCommentSyntax(filePath) {
|
|
18
|
+
const extension = extname(filePath).toLowerCase();
|
|
19
|
+
if (extensions.includes(extension)) {
|
|
20
|
+
return {
|
|
21
|
+
kind: "block",
|
|
22
|
+
blockStart: "/**",
|
|
23
|
+
blockLinePrefix: " *\t",
|
|
24
|
+
blockEnd: " */"
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const detector = {
|
|
31
|
+
id: "go",
|
|
32
|
+
priority: 80,
|
|
33
|
+
markers,
|
|
34
|
+
extensions,
|
|
35
|
+
enabledByDefault: true,
|
|
36
|
+
findNearestConfig(startPath) {
|
|
37
|
+
return findNearestMarker(startPath, markers);
|
|
38
|
+
},
|
|
39
|
+
parseProjectName(_marker, markerContent, rootDirName) {
|
|
40
|
+
const moduleMatch = markerContent.match(/^module\s+(.+)$/m);
|
|
41
|
+
if (moduleMatch?.[1]) {
|
|
42
|
+
const value = moduleMatch[1].trim();
|
|
43
|
+
if (value.length > 0) {
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return rootDirName;
|
|
48
|
+
},
|
|
49
|
+
resolveCommentSyntax(filePath) {
|
|
50
|
+
return resolveGoCommentSyntax(filePath);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { extname } from "node:path";
|
|
2
|
+
import { findNearestMarker } from "./shared.mjs";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @fileoverview HTML detector implementation.
|
|
6
|
+
* @module fix-headers/detectors/html
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const markers = ["index.html", "vite.config.js", "vite.config.mjs", "next.config.js", "next.config.mjs"];
|
|
10
|
+
const extensions = [".html", ".htm"];
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Parses HTML project name from marker context.
|
|
14
|
+
* @param {string} rootDirName - Fallback directory name.
|
|
15
|
+
* @returns {string} Project name.
|
|
16
|
+
*/
|
|
17
|
+
function parseHtmlProjectName(rootDirName) {
|
|
18
|
+
return rootDirName;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Resolves HTML comment syntax for HTML-like files.
|
|
23
|
+
* @param {string} filePath - File path.
|
|
24
|
+
* @returns {{kind: "html", blockStart: string, blockLinePrefix: string, blockEnd: string} | null} Syntax descriptor.
|
|
25
|
+
*/
|
|
26
|
+
function resolveHtmlCommentSyntax(filePath) {
|
|
27
|
+
const extension = extname(filePath).toLowerCase();
|
|
28
|
+
if (extensions.includes(extension)) {
|
|
29
|
+
return {
|
|
30
|
+
kind: "html",
|
|
31
|
+
blockStart: "<!--",
|
|
32
|
+
blockLinePrefix: "\t",
|
|
33
|
+
blockEnd: "-->"
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export const detector = {
|
|
40
|
+
id: "html",
|
|
41
|
+
priority: 70,
|
|
42
|
+
markers,
|
|
43
|
+
extensions,
|
|
44
|
+
enabledByDefault: true,
|
|
45
|
+
findNearestConfig(startPath) {
|
|
46
|
+
return findNearestMarker(startPath, markers);
|
|
47
|
+
},
|
|
48
|
+
parseProjectName(_marker, _markerContent, rootDirName) {
|
|
49
|
+
return parseHtmlProjectName(rootDirName);
|
|
50
|
+
},
|
|
51
|
+
resolveCommentSyntax(filePath) {
|
|
52
|
+
return resolveHtmlCommentSyntax(filePath);
|
|
53
|
+
}
|
|
54
|
+
};
|