console-sniper 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/LICENSE +21 -0
- package/README.md +226 -0
- package/dist/cli/cli.js +493 -0
- package/dist/cli/cli.js.map +1 -0
- package/dist/index.cjs +250 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +93 -0
- package/dist/index.d.ts +93 -0
- package/dist/index.js +204 -0
- package/dist/index.js.map +1 -0
- package/dist/types-J-TZoPXb.d.cts +133 -0
- package/dist/types-J-TZoPXb.d.ts +133 -0
- package/dist/vite/vitePlugin.cjs +289 -0
- package/dist/vite/vitePlugin.cjs.map +1 -0
- package/dist/vite/vitePlugin.d.cts +51 -0
- package/dist/vite/vitePlugin.d.ts +51 -0
- package/dist/vite/vitePlugin.js +252 -0
- package/dist/vite/vitePlugin.js.map +1 -0
- package/package.json +90 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var src_exports = {};
|
|
32
|
+
__export(src_exports, {
|
|
33
|
+
DEFAULT_METHODS: () => DEFAULT_METHODS,
|
|
34
|
+
PACKAGE_NAME: () => PACKAGE_NAME,
|
|
35
|
+
PACKAGE_VERSION: () => PACKAGE_VERSION,
|
|
36
|
+
SUPPORTED_EXTENSIONS: () => SUPPORTED_EXTENSIONS,
|
|
37
|
+
formatCount: () => formatCount,
|
|
38
|
+
isSupportedFile: () => isSupportedFile,
|
|
39
|
+
parseMethodList: () => parseMethodList,
|
|
40
|
+
resolveOptionsSync: () => resolveOptionsSync,
|
|
41
|
+
shouldProcessFile: () => shouldProcessFile,
|
|
42
|
+
stripConsoleFromCode: () => stripConsoleFromCode
|
|
43
|
+
});
|
|
44
|
+
module.exports = __toCommonJS(src_exports);
|
|
45
|
+
|
|
46
|
+
// src/core/stripConsole.ts
|
|
47
|
+
var import_parser = require("@babel/parser");
|
|
48
|
+
var import_traverse = __toESM(require("@babel/traverse"), 1);
|
|
49
|
+
var import_generator = __toESM(require("@babel/generator"), 1);
|
|
50
|
+
var t = __toESM(require("@babel/types"), 1);
|
|
51
|
+
|
|
52
|
+
// src/core/constants.ts
|
|
53
|
+
var PACKAGE_NAME = "console-sniper";
|
|
54
|
+
var PACKAGE_VERSION = "1.0.0";
|
|
55
|
+
var DEFAULT_METHODS = [
|
|
56
|
+
"log",
|
|
57
|
+
"warn",
|
|
58
|
+
"error",
|
|
59
|
+
"info",
|
|
60
|
+
"debug"
|
|
61
|
+
];
|
|
62
|
+
var DEFAULT_REMOVE_COMMENTS = true;
|
|
63
|
+
var SUPPORTED_EXTENSIONS = [
|
|
64
|
+
".js",
|
|
65
|
+
".mjs",
|
|
66
|
+
".cjs",
|
|
67
|
+
".jsx",
|
|
68
|
+
".ts",
|
|
69
|
+
".tsx",
|
|
70
|
+
".mts",
|
|
71
|
+
".cts"
|
|
72
|
+
];
|
|
73
|
+
var CONSOLE_KEYWORD = "console";
|
|
74
|
+
|
|
75
|
+
// src/core/utils.ts
|
|
76
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
77
|
+
function resolveOptionsSync(options) {
|
|
78
|
+
return {
|
|
79
|
+
methods: options?.methods?.length ? options.methods : [...DEFAULT_METHODS],
|
|
80
|
+
removeComments: options?.removeComments ?? DEFAULT_REMOVE_COMMENTS,
|
|
81
|
+
include: options?.include ?? [],
|
|
82
|
+
exclude: options?.exclude ?? [],
|
|
83
|
+
silent: options?.silent ?? false
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function isSupportedFile(filePath) {
|
|
87
|
+
const ext = import_node_path.default.extname(filePath).toLowerCase();
|
|
88
|
+
return SUPPORTED_EXTENSIONS.includes(ext);
|
|
89
|
+
}
|
|
90
|
+
function shouldProcessFile(filePath, include = [], exclude = []) {
|
|
91
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
92
|
+
if (include.length > 0 && !include.some((p) => p.test(normalized))) return false;
|
|
93
|
+
if (exclude.length > 0 && exclude.some((p) => p.test(normalized))) return false;
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
function parseMethodList(input) {
|
|
97
|
+
return input.split(",").map((m) => m.trim()).filter((m) => m.length > 0);
|
|
98
|
+
}
|
|
99
|
+
function formatCount(count, noun) {
|
|
100
|
+
return `${count} ${noun}${count !== 1 ? "s" : ""}`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// src/core/stripConsole.ts
|
|
104
|
+
var traverse = typeof import_traverse.default === "function" ? import_traverse.default : import_traverse.default.default;
|
|
105
|
+
var generate = typeof import_generator.default === "function" ? import_generator.default : import_generator.default.default;
|
|
106
|
+
function stripConsoleFromCode(code, options) {
|
|
107
|
+
const opts = resolveOptionsSync(options);
|
|
108
|
+
const methodsToRemove = new Set(opts.methods);
|
|
109
|
+
const removedMethods = [];
|
|
110
|
+
const parserOptions = {
|
|
111
|
+
sourceType: "module",
|
|
112
|
+
// Support import/export statements
|
|
113
|
+
strictMode: false,
|
|
114
|
+
// Don't throw on sloppy-mode code
|
|
115
|
+
allowImportExportEverywhere: true,
|
|
116
|
+
allowReturnOutsideFunction: true,
|
|
117
|
+
allowSuperOutsideMethod: true,
|
|
118
|
+
plugins: [
|
|
119
|
+
"typescript",
|
|
120
|
+
// TypeScript syntax (types, generics, decorators)
|
|
121
|
+
"jsx",
|
|
122
|
+
// JSX/TSX syntax
|
|
123
|
+
"decorators",
|
|
124
|
+
// @decorator syntax
|
|
125
|
+
"classProperties",
|
|
126
|
+
"classPrivateProperties",
|
|
127
|
+
"classPrivateMethods",
|
|
128
|
+
"classStaticBlock",
|
|
129
|
+
"dynamicImport",
|
|
130
|
+
// import(...)
|
|
131
|
+
"exportDefaultFrom",
|
|
132
|
+
"exportNamespaceFrom",
|
|
133
|
+
"nullishCoalescingOperator",
|
|
134
|
+
// ??
|
|
135
|
+
"optionalChaining",
|
|
136
|
+
// ?.
|
|
137
|
+
"optionalCatchBinding",
|
|
138
|
+
"logicalAssignment",
|
|
139
|
+
// &&=, ||=, ??=
|
|
140
|
+
"numericSeparator",
|
|
141
|
+
// 1_000_000
|
|
142
|
+
"bigInt",
|
|
143
|
+
"importMeta",
|
|
144
|
+
// import.meta
|
|
145
|
+
"topLevelAwait"
|
|
146
|
+
]
|
|
147
|
+
};
|
|
148
|
+
let ast;
|
|
149
|
+
try {
|
|
150
|
+
ast = (0, import_parser.parse)(code, parserOptions);
|
|
151
|
+
} catch (parseError) {
|
|
152
|
+
if (!opts.silent) {
|
|
153
|
+
console.warn(
|
|
154
|
+
`[console-sniper] Failed to parse code, skipping. Error: ${parseError instanceof Error ? parseError.message : String(parseError)}`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
return {
|
|
158
|
+
code,
|
|
159
|
+
removedCount: 0,
|
|
160
|
+
removedMethods: [],
|
|
161
|
+
changed: false
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
traverse(ast, {
|
|
165
|
+
ExpressionStatement(nodePath) {
|
|
166
|
+
const { expression } = nodePath.node;
|
|
167
|
+
if (!t.isCallExpression(expression)) return;
|
|
168
|
+
const { callee } = expression;
|
|
169
|
+
if (!t.isMemberExpression(callee)) return;
|
|
170
|
+
const { object, property } = callee;
|
|
171
|
+
if (!t.isIdentifier(object, { name: "console" })) return;
|
|
172
|
+
let methodName;
|
|
173
|
+
if (t.isIdentifier(property)) {
|
|
174
|
+
methodName = property.name;
|
|
175
|
+
} else if (t.isStringLiteral(property)) {
|
|
176
|
+
methodName = property.value;
|
|
177
|
+
}
|
|
178
|
+
if (!methodName || !methodsToRemove.has(methodName)) return;
|
|
179
|
+
removedMethods.push(methodName);
|
|
180
|
+
nodePath.remove();
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
if (opts.removeComments) {
|
|
184
|
+
removeConsoleComments(ast);
|
|
185
|
+
}
|
|
186
|
+
const { code: transformedCode } = generate(
|
|
187
|
+
ast,
|
|
188
|
+
{
|
|
189
|
+
retainLines: false,
|
|
190
|
+
// Compact output; set true if you need line parity
|
|
191
|
+
compact: false,
|
|
192
|
+
// Keep human-readable whitespace
|
|
193
|
+
concise: false,
|
|
194
|
+
comments: true,
|
|
195
|
+
// Include non-console comments
|
|
196
|
+
jsescOption: {
|
|
197
|
+
minimal: true
|
|
198
|
+
// Don't over-escape unicode
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
code
|
|
202
|
+
// Original source (used for sourcemap generation)
|
|
203
|
+
);
|
|
204
|
+
const removedCount = removedMethods.length;
|
|
205
|
+
return {
|
|
206
|
+
code: transformedCode,
|
|
207
|
+
removedCount,
|
|
208
|
+
removedMethods,
|
|
209
|
+
changed: removedCount > 0
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
function removeConsoleComments(ast) {
|
|
213
|
+
function filterComments(comments) {
|
|
214
|
+
if (!comments || comments.length === 0) return comments ?? null;
|
|
215
|
+
return comments.filter(
|
|
216
|
+
(comment) => !comment.value.includes(CONSOLE_KEYWORD)
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
traverse(ast, {
|
|
220
|
+
enter(nodePath) {
|
|
221
|
+
const { node } = nodePath;
|
|
222
|
+
if (node.leadingComments) {
|
|
223
|
+
const filtered = filterComments(node.leadingComments);
|
|
224
|
+
node.leadingComments = filtered;
|
|
225
|
+
}
|
|
226
|
+
if (node.trailingComments) {
|
|
227
|
+
const filtered = filterComments(node.trailingComments);
|
|
228
|
+
node.trailingComments = filtered;
|
|
229
|
+
}
|
|
230
|
+
if (node.innerComments) {
|
|
231
|
+
const filtered = filterComments(node.innerComments);
|
|
232
|
+
node.innerComments = filtered;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
238
|
+
0 && (module.exports = {
|
|
239
|
+
DEFAULT_METHODS,
|
|
240
|
+
PACKAGE_NAME,
|
|
241
|
+
PACKAGE_VERSION,
|
|
242
|
+
SUPPORTED_EXTENSIONS,
|
|
243
|
+
formatCount,
|
|
244
|
+
isSupportedFile,
|
|
245
|
+
parseMethodList,
|
|
246
|
+
resolveOptionsSync,
|
|
247
|
+
shouldProcessFile,
|
|
248
|
+
stripConsoleFromCode
|
|
249
|
+
});
|
|
250
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core/stripConsole.ts","../src/core/constants.ts","../src/core/utils.ts"],"sourcesContent":["/**\r\n * @file index.ts\r\n * @description The main public API entry point for console-sniper.\r\n *\r\n * This file re-exports everything that external consumers might need.\r\n * Think of it as the \"public interface\" of the package.\r\n *\r\n * Usage:\r\n * import { stripConsoleFromCode } from \"console-sniper\"\r\n * import type { StripConsoleOptions, StripConsoleResult } from \"console-sniper\"\r\n *\r\n * The Vite plugin is intentionally NOT exported from here — it lives at\r\n * \"console-sniper/vite\" to keep Vite as an optional peer dependency.\r\n */\r\n\r\n// Core Engine\r\nexport { stripConsoleFromCode } from \"./core/stripConsole.js\";\r\n\r\n// Types\r\nexport type {\r\n StripConsoleOptions,\r\n StripConsoleResult,\r\n FileProcessResult,\r\n BatchSummary,\r\n VitePluginOptions,\r\n CliOptions,\r\n} from \"./core/types.js\";\r\n\r\n// Constants\r\nexport {\r\n DEFAULT_METHODS,\r\n SUPPORTED_EXTENSIONS,\r\n PACKAGE_NAME,\r\n PACKAGE_VERSION,\r\n} from \"./core/constants.js\";\r\n\r\n// Utilities\r\nexport {\r\n resolveOptionsSync,\r\n isSupportedFile,\r\n shouldProcessFile,\r\n parseMethodList,\r\n formatCount,\r\n} from \"./core/utils.js\";\r\n","/**\r\n * @file stripConsole.ts\r\n * @description The AST-based core engine that removes console statements.\r\n *\r\n * ─── Architecture Note ────────────────────────────────────────────────────────\r\n * This file is the heart of console-sniper. It has ZERO dependencies on Vite,\r\n * the CLI, or Node.js file system APIs. It works purely on source code strings.\r\n *\r\n * This isolation means the same engine can power:\r\n * • The Vite plugin (src/vite/vitePlugin.ts)\r\n * • The CLI (src/cli/cli.ts)\r\n * • Future: Rollup, Webpack, esbuild, Bun plugins\r\n * • Any programmatic usage\r\n * ─────────────────────────────────────────────────────────────────────────────\r\n *\r\n * How it works (at a high level):\r\n * 1. Parse the source code into an AST (Abstract Syntax Tree)\r\n * 2. Walk the AST looking for `ExpressionStatement` nodes where the\r\n * expression is a `CallExpression` like `console.log(...)`\r\n * 3. Remove those nodes from the AST\r\n * 4. Optionally scan and remove comments referencing \"console\"\r\n * 5. Re-generate source code from the modified AST\r\n */\r\n\r\nimport { parse, type ParserOptions } from \"@babel/parser\";\r\nimport _traverse from \"@babel/traverse\";\r\nimport _generate from \"@babel/generator\";\r\nimport * as t from \"@babel/types\";\r\n\r\nimport { CONSOLE_KEYWORD } from \"./constants.js\";\r\nimport { resolveOptionsSync } from \"./utils.js\";\r\nimport type { StripConsoleOptions, StripConsoleResult } from \"./types.js\";\r\n\r\n\r\n// CJS/ESM interop fix\r\n\r\n// @babel/traverse and @babel/generator are CommonJS packages. When this\r\n// module is bundled as ESM (or loaded via ts-node/vitest), the default\r\n// import may resolve to the module object rather than the callable function.\r\n// The actual function is always available on `.default` in that case.\r\n// We normalise once here so every call site just works.\r\nconst traverse = (\r\n typeof _traverse === \"function\" ? _traverse : (_traverse as any).default\r\n) as typeof _traverse;\r\n\r\nconst generate = (\r\n typeof _generate === \"function\" ? _generate : (_generate as any).default\r\n) as typeof _generate;\r\n\r\n\r\n// Public API\r\n\r\n/**\r\n * Strip `console.*` calls from a source code string using AST parsing.\r\n *\r\n * This is the primary public API of console-sniper. It's pure and stateless —\r\n * give it code, get transformed code back. Nothing is written to disk here.\r\n *\r\n * @param code - The source code to transform (JS, TS, JSX, TSX)\r\n * @param options - Optional configuration\r\n * @returns - The transformed code + metadata about what was removed\r\n *\r\n * @example\r\n * ```ts\r\n * const { code, removedCount, changed } = stripConsoleFromCode(`\r\n * console.log(\"hello\");\r\n * const x = 1 + 2;\r\n * console.warn(\"something\");\r\n * `);\r\n * // code → \"\\nconst x = 1 + 2;\\n\"\r\n * // removed → 2\r\n * // changed → true\r\n * ```\r\n */\r\nexport function stripConsoleFromCode(\r\n code: string,\r\n options?: StripConsoleOptions\r\n): StripConsoleResult {\r\n // 1. Resolve options (fills in defaults for anything not specified)\r\n const opts = resolveOptionsSync(options);\r\n const methodsToRemove = new Set(opts.methods);\r\n\r\n // Track what we remove for the metadata result\r\n const removedMethods: string[] = [];\r\n\r\n // ── Step 1: Parse ────────────────────────────────────────────\r\n // We need to tell Babel's parser about all the syntax we might encounter.\r\n // Using all these plugins means a single parser config handles every file\r\n // type (JS, TS, JSX, TSX) without needing per-file configuration.\r\n const parserOptions: ParserOptions = {\r\n sourceType: \"module\", // Support import/export statements\r\n strictMode: false, // Don't throw on sloppy-mode code\r\n allowImportExportEverywhere: true,\r\n allowReturnOutsideFunction: true,\r\n allowSuperOutsideMethod: true,\r\n plugins: [\r\n \"typescript\", // TypeScript syntax (types, generics, decorators)\r\n \"jsx\", // JSX/TSX syntax\r\n \"decorators\", // @decorator syntax\r\n \"classProperties\",\r\n \"classPrivateProperties\",\r\n \"classPrivateMethods\",\r\n \"classStaticBlock\",\r\n \"dynamicImport\", // import(...)\r\n \"exportDefaultFrom\",\r\n \"exportNamespaceFrom\",\r\n \"nullishCoalescingOperator\", // ??\r\n \"optionalChaining\", // ?.\r\n \"optionalCatchBinding\",\r\n \"logicalAssignment\", // &&=, ||=, ??=\r\n \"numericSeparator\", // 1_000_000\r\n \"bigInt\",\r\n \"importMeta\", // import.meta\r\n \"topLevelAwait\",\r\n ],\r\n };\r\n\r\n let ast: ReturnType<typeof parse>;\r\n\r\n try {\r\n ast = parse(code, parserOptions);\r\n } catch (parseError) {\r\n // If parsing fails (e.g. unsupported syntax), return the original code\r\n // unchanged rather than crashing. This is safer for production builds.\r\n if (!opts.silent) {\r\n console.warn(\r\n `[console-sniper] Failed to parse code, skipping. Error: ${\r\n parseError instanceof Error ? parseError.message : String(parseError)\r\n }`\r\n );\r\n }\r\n return {\r\n code,\r\n removedCount: 0,\r\n removedMethods: [],\r\n changed: false,\r\n };\r\n }\r\n\r\n // ── Step 2: Traverse & Remove Console Calls ─────────────────\r\n //\r\n // We look for nodes matching this AST pattern:\r\n //\r\n // ExpressionStatement\r\n // └── CallExpression\r\n // ├── callee: MemberExpression\r\n // │ ├── object: Identifier { name: \"console\" }\r\n // │ └── property: Identifier { name: \"log\" | \"warn\" | ... }\r\n // └── arguments: [...]\r\n //\r\n // Matching the full ExpressionStatement (the statement wrapper) rather than\r\n // just the CallExpression lets us safely remove the entire line — including\r\n // the trailing semicolon — without leaving orphan syntax behind.\r\n\r\n traverse(ast, {\r\n ExpressionStatement(nodePath) {\r\n const { expression } = nodePath.node;\r\n\r\n // Must be a function call\r\n if (!t.isCallExpression(expression)) return;\r\n\r\n const { callee } = expression;\r\n\r\n // The callee must be a member expression (object.method)\r\n if (!t.isMemberExpression(callee)) return;\r\n\r\n const { object, property } = callee;\r\n\r\n // The object must be `console` (an Identifier with name \"console\")\r\n if (!t.isIdentifier(object, { name: \"console\" })) return;\r\n\r\n // The property must be one of our configured methods\r\n // Property can be either an Identifier (console.log) or\r\n // a StringLiteral (console[\"log\"]) — we handle both.\r\n let methodName: string | undefined;\r\n\r\n if (t.isIdentifier(property)) {\r\n methodName = property.name;\r\n } else if (t.isStringLiteral(property)) {\r\n methodName = property.value;\r\n }\r\n\r\n if (!methodName || !methodsToRemove.has(methodName)) return;\r\n\r\n // ✅ This is a console call we should remove!\r\n removedMethods.push(methodName);\r\n\r\n // `nodePath.remove()` is the safe Babel way to delete a node.\r\n // It properly handles parent references, scope bindings, etc.\r\n nodePath.remove();\r\n },\r\n });\r\n\r\n // ── Step 3: Remove Console Comments ─────────────────────────\r\n //\r\n // Babel attaches comments to the nearest AST node as `leadingComments`\r\n // or `trailingComments`. We need to scan ALL nodes and filter out\r\n // any comments that mention \"console\".\r\n //\r\n // We do this AFTER the traverse so we don't interfere with node removal.\r\n\r\n if (opts.removeComments) {\r\n removeConsoleComments(ast);\r\n }\r\n\r\n // ── Step 4: Re-generate Source Code ─────────────────────────\r\n //\r\n // @babel/generator walks the (now modified) AST and prints it back to a\r\n // string. We enable `retainLines` to preserve line numbers as much as\r\n // possible — this keeps source maps accurate and diffs readable.\r\n\r\n const { code: transformedCode } = generate(\r\n ast,\r\n {\r\n retainLines: false, // Compact output; set true if you need line parity\r\n compact: false, // Keep human-readable whitespace\r\n concise: false,\r\n comments: true, // Include non-console comments\r\n jsescOption: {\r\n minimal: true, // Don't over-escape unicode\r\n },\r\n },\r\n code // Original source (used for sourcemap generation)\r\n );\r\n\r\n const removedCount = removedMethods.length;\r\n\r\n return {\r\n code: transformedCode,\r\n removedCount,\r\n removedMethods,\r\n changed: removedCount > 0,\r\n };\r\n}\r\n\r\n// Internal Helpers\r\n\r\n/**\r\n * Walk the AST and strip comments that reference \"console\".\r\n *\r\n * Babel stores comments as arrays on AST nodes:\r\n * node.leadingComments — comments before the node\r\n * node.innerComments — comments inside empty blocks\r\n * node.trailingComments — comments after the node\r\n *\r\n * We filter each array, removing comments whose `value` contains the\r\n * CONSOLE_KEYWORD (\"console\").\r\n *\r\n * @param ast - The parsed AST (mutated in place)\r\n */\r\nfunction removeConsoleComments(ast: t.File): void {\r\n /**\r\n * Filter a comment array, removing any that mention \"console\".\r\n */\r\n function filterComments(\r\n comments: t.Comment[] | null | undefined\r\n ): t.Comment[] | null {\r\n if (!comments || comments.length === 0) return comments ?? null;\r\n\r\n return comments.filter(\r\n (comment) => !comment.value.includes(CONSOLE_KEYWORD)\r\n );\r\n }\r\n\r\n // We need to visit every node in the AST.\r\n // `traverse` with no specific node type visits everything.\r\n traverse(ast, {\r\n enter(nodePath) {\r\n const { node } = nodePath;\r\n\r\n // Filter leading comments (e.g. `// console.log here`)\r\n if (node.leadingComments) {\r\n const filtered = filterComments(node.leadingComments);\r\n // Babel uses `null` when there are no comments\r\n (node as t.Node & { leadingComments: t.Comment[] | null }).leadingComments =\r\n filtered;\r\n }\r\n\r\n // Filter trailing comments (e.g. `} // end console block`)\r\n if (node.trailingComments) {\r\n const filtered = filterComments(node.trailingComments);\r\n (node as t.Node & { trailingComments: t.Comment[] | null }).trailingComments =\r\n filtered;\r\n }\r\n\r\n // Filter inner comments (e.g. comments inside empty blocks)\r\n if (node.innerComments) {\r\n const filtered = filterComments(node.innerComments);\r\n (node as t.Node & { innerComments: t.Comment[] | null }).innerComments =\r\n filtered;\r\n }\r\n },\r\n });\r\n}\r\n","/**\r\n * @file constants.ts\r\n * @description Global constants used across the project.\r\n *\r\n * Centralizing magic strings and default values here prevents typos\r\n * and makes it trivial to change defaults in one place.\r\n */\r\n\r\n\r\n// Package Identity\r\n\r\n/** The package name — used in logs, banners, and plugin names. */\r\nexport const PACKAGE_NAME = \"console-sniper\";\r\n\r\n/** Current version — ideally sync'd with package.json at build time. */\r\nexport const PACKAGE_VERSION = \"1.0.0\";\r\n\r\n\r\n// Console Stripping Defaults\r\n\r\n/**\r\n * Default console methods that are removed when `methods` is not specified.\r\n *\r\n * Extending this list is the primary way to support new console variants\r\n * like `console.table`, `console.time`, `console.group`, etc.\r\n */\r\nexport const DEFAULT_METHODS: readonly string[] = [\r\n \"log\",\r\n \"warn\",\r\n \"error\",\r\n \"info\",\r\n \"debug\",\r\n] as const;\r\n\r\n/**\r\n * Whether to remove comments containing \"console\" by default.\r\n */\r\nexport const DEFAULT_REMOVE_COMMENTS = true;\r\n\r\n\r\n// File Extension Filters\r\n\r\n/**\r\n * File extensions that the Babel parser can handle.\r\n * These are used by the CLI file scanner to filter which files to process.\r\n *\r\n * Note: The Babel parser handles TypeScript and JSX natively via plugins.\r\n */\r\nexport const SUPPORTED_EXTENSIONS: readonly string[] = [\r\n \".js\",\r\n \".mjs\",\r\n \".cjs\",\r\n \".jsx\",\r\n \".ts\",\r\n \".tsx\",\r\n \".mts\",\r\n \".cts\",\r\n] as const;\r\n\r\n/**\r\n * Default glob patterns for the CLI to EXCLUDE when scanning directories.\r\n */\r\nexport const DEFAULT_EXCLUDE_PATTERNS: readonly string[] = [\r\n \"**/node_modules/**\",\r\n \"**/dist/**\",\r\n \"**/build/**\",\r\n \"**/.git/**\",\r\n \"**/*.min.js\",\r\n \"**/*.d.ts\",\r\n] as const;\r\n\r\n// AST Parser Config\r\n\r\n/**\r\n * The word we look for when scanning comment text.\r\n * Keeping this as a constant prevents subtle bugs from typos.\r\n */\r\nexport const CONSOLE_KEYWORD = \"console\";\r\n","/**\r\n * @file utils.ts\r\n * @description Pure utility functions shared across the core engine.\r\n */\r\n\r\nimport path from \"node:path\";\r\nimport { SUPPORTED_EXTENSIONS, DEFAULT_METHODS, DEFAULT_REMOVE_COMMENTS } from \"./constants.js\";\r\nimport type { StripConsoleOptions } from \"./types.js\";\r\n\r\nexport function resolveOptionsSync(\r\n options?: StripConsoleOptions\r\n): StripConsoleOptions & {\r\n methods: string[];\r\n removeComments: boolean;\r\n silent: boolean;\r\n} {\r\n return {\r\n methods: options?.methods?.length ? options.methods : [...DEFAULT_METHODS],\r\n removeComments: options?.removeComments ?? DEFAULT_REMOVE_COMMENTS,\r\n include: options?.include ?? [],\r\n exclude: options?.exclude ?? [],\r\n silent: options?.silent ?? false,\r\n };\r\n}\r\n\r\nexport function isSupportedFile(filePath: string): boolean {\r\n const ext = path.extname(filePath).toLowerCase();\r\n return (SUPPORTED_EXTENSIONS as readonly string[]).includes(ext);\r\n}\r\n\r\nexport function shouldProcessFile(\r\n filePath: string,\r\n include: RegExp[] = [],\r\n exclude: RegExp[] = []\r\n): boolean {\r\n const normalized = filePath.replace(/\\\\/g, \"/\");\r\n if (include.length > 0 && !include.some((p) => p.test(normalized))) return false;\r\n if (exclude.length > 0 && exclude.some((p) => p.test(normalized))) return false;\r\n return true;\r\n}\r\n\r\nexport function parseMethodList(input: string): string[] {\r\n return input.split(\",\").map((m) => m.trim()).filter((m) => m.length > 0);\r\n}\r\n\r\nexport function formatCount(count: number, noun: string): string {\r\n return `${count} ${noun}${count !== 1 ? \"s\" : \"\"}`;\r\n}\r\n\r\nexport function isNode(): boolean {\r\n return typeof process !== \"undefined\" && process.versions?.node != null;\r\n}\r\n"],"mappings":"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","names":["path","_traverse","_generate"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { S as StripConsoleOptions, a as StripConsoleResult } from './types-J-TZoPXb.cjs';
|
|
2
|
+
export { B as BatchSummary, C as CliOptions, F as FileProcessResult, V as VitePluginOptions } from './types-J-TZoPXb.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @file stripConsole.ts
|
|
6
|
+
* @description The AST-based core engine that removes console statements.
|
|
7
|
+
*
|
|
8
|
+
* ─── Architecture Note ────────────────────────────────────────────────────────
|
|
9
|
+
* This file is the heart of console-sniper. It has ZERO dependencies on Vite,
|
|
10
|
+
* the CLI, or Node.js file system APIs. It works purely on source code strings.
|
|
11
|
+
*
|
|
12
|
+
* This isolation means the same engine can power:
|
|
13
|
+
* • The Vite plugin (src/vite/vitePlugin.ts)
|
|
14
|
+
* • The CLI (src/cli/cli.ts)
|
|
15
|
+
* • Future: Rollup, Webpack, esbuild, Bun plugins
|
|
16
|
+
* • Any programmatic usage
|
|
17
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
18
|
+
*
|
|
19
|
+
* How it works (at a high level):
|
|
20
|
+
* 1. Parse the source code into an AST (Abstract Syntax Tree)
|
|
21
|
+
* 2. Walk the AST looking for `ExpressionStatement` nodes where the
|
|
22
|
+
* expression is a `CallExpression` like `console.log(...)`
|
|
23
|
+
* 3. Remove those nodes from the AST
|
|
24
|
+
* 4. Optionally scan and remove comments referencing "console"
|
|
25
|
+
* 5. Re-generate source code from the modified AST
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Strip `console.*` calls from a source code string using AST parsing.
|
|
30
|
+
*
|
|
31
|
+
* This is the primary public API of console-sniper. It's pure and stateless —
|
|
32
|
+
* give it code, get transformed code back. Nothing is written to disk here.
|
|
33
|
+
*
|
|
34
|
+
* @param code - The source code to transform (JS, TS, JSX, TSX)
|
|
35
|
+
* @param options - Optional configuration
|
|
36
|
+
* @returns - The transformed code + metadata about what was removed
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* const { code, removedCount, changed } = stripConsoleFromCode(`
|
|
41
|
+
* console.log("hello");
|
|
42
|
+
* const x = 1 + 2;
|
|
43
|
+
* console.warn("something");
|
|
44
|
+
* `);
|
|
45
|
+
* // code → "\nconst x = 1 + 2;\n"
|
|
46
|
+
* // removed → 2
|
|
47
|
+
* // changed → true
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
declare function stripConsoleFromCode(code: string, options?: StripConsoleOptions): StripConsoleResult;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @file constants.ts
|
|
54
|
+
* @description Global constants used across the project.
|
|
55
|
+
*
|
|
56
|
+
* Centralizing magic strings and default values here prevents typos
|
|
57
|
+
* and makes it trivial to change defaults in one place.
|
|
58
|
+
*/
|
|
59
|
+
/** The package name — used in logs, banners, and plugin names. */
|
|
60
|
+
declare const PACKAGE_NAME = "console-sniper";
|
|
61
|
+
/** Current version — ideally sync'd with package.json at build time. */
|
|
62
|
+
declare const PACKAGE_VERSION = "1.0.0";
|
|
63
|
+
/**
|
|
64
|
+
* Default console methods that are removed when `methods` is not specified.
|
|
65
|
+
*
|
|
66
|
+
* Extending this list is the primary way to support new console variants
|
|
67
|
+
* like `console.table`, `console.time`, `console.group`, etc.
|
|
68
|
+
*/
|
|
69
|
+
declare const DEFAULT_METHODS: readonly string[];
|
|
70
|
+
/**
|
|
71
|
+
* File extensions that the Babel parser can handle.
|
|
72
|
+
* These are used by the CLI file scanner to filter which files to process.
|
|
73
|
+
*
|
|
74
|
+
* Note: The Babel parser handles TypeScript and JSX natively via plugins.
|
|
75
|
+
*/
|
|
76
|
+
declare const SUPPORTED_EXTENSIONS: readonly string[];
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @file utils.ts
|
|
80
|
+
* @description Pure utility functions shared across the core engine.
|
|
81
|
+
*/
|
|
82
|
+
|
|
83
|
+
declare function resolveOptionsSync(options?: StripConsoleOptions): StripConsoleOptions & {
|
|
84
|
+
methods: string[];
|
|
85
|
+
removeComments: boolean;
|
|
86
|
+
silent: boolean;
|
|
87
|
+
};
|
|
88
|
+
declare function isSupportedFile(filePath: string): boolean;
|
|
89
|
+
declare function shouldProcessFile(filePath: string, include?: RegExp[], exclude?: RegExp[]): boolean;
|
|
90
|
+
declare function parseMethodList(input: string): string[];
|
|
91
|
+
declare function formatCount(count: number, noun: string): string;
|
|
92
|
+
|
|
93
|
+
export { DEFAULT_METHODS, PACKAGE_NAME, PACKAGE_VERSION, SUPPORTED_EXTENSIONS, StripConsoleOptions, StripConsoleResult, formatCount, isSupportedFile, parseMethodList, resolveOptionsSync, shouldProcessFile, stripConsoleFromCode };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { S as StripConsoleOptions, a as StripConsoleResult } from './types-J-TZoPXb.js';
|
|
2
|
+
export { B as BatchSummary, C as CliOptions, F as FileProcessResult, V as VitePluginOptions } from './types-J-TZoPXb.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @file stripConsole.ts
|
|
6
|
+
* @description The AST-based core engine that removes console statements.
|
|
7
|
+
*
|
|
8
|
+
* ─── Architecture Note ────────────────────────────────────────────────────────
|
|
9
|
+
* This file is the heart of console-sniper. It has ZERO dependencies on Vite,
|
|
10
|
+
* the CLI, or Node.js file system APIs. It works purely on source code strings.
|
|
11
|
+
*
|
|
12
|
+
* This isolation means the same engine can power:
|
|
13
|
+
* • The Vite plugin (src/vite/vitePlugin.ts)
|
|
14
|
+
* • The CLI (src/cli/cli.ts)
|
|
15
|
+
* • Future: Rollup, Webpack, esbuild, Bun plugins
|
|
16
|
+
* • Any programmatic usage
|
|
17
|
+
* ─────────────────────────────────────────────────────────────────────────────
|
|
18
|
+
*
|
|
19
|
+
* How it works (at a high level):
|
|
20
|
+
* 1. Parse the source code into an AST (Abstract Syntax Tree)
|
|
21
|
+
* 2. Walk the AST looking for `ExpressionStatement` nodes where the
|
|
22
|
+
* expression is a `CallExpression` like `console.log(...)`
|
|
23
|
+
* 3. Remove those nodes from the AST
|
|
24
|
+
* 4. Optionally scan and remove comments referencing "console"
|
|
25
|
+
* 5. Re-generate source code from the modified AST
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Strip `console.*` calls from a source code string using AST parsing.
|
|
30
|
+
*
|
|
31
|
+
* This is the primary public API of console-sniper. It's pure and stateless —
|
|
32
|
+
* give it code, get transformed code back. Nothing is written to disk here.
|
|
33
|
+
*
|
|
34
|
+
* @param code - The source code to transform (JS, TS, JSX, TSX)
|
|
35
|
+
* @param options - Optional configuration
|
|
36
|
+
* @returns - The transformed code + metadata about what was removed
|
|
37
|
+
*
|
|
38
|
+
* @example
|
|
39
|
+
* ```ts
|
|
40
|
+
* const { code, removedCount, changed } = stripConsoleFromCode(`
|
|
41
|
+
* console.log("hello");
|
|
42
|
+
* const x = 1 + 2;
|
|
43
|
+
* console.warn("something");
|
|
44
|
+
* `);
|
|
45
|
+
* // code → "\nconst x = 1 + 2;\n"
|
|
46
|
+
* // removed → 2
|
|
47
|
+
* // changed → true
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
declare function stripConsoleFromCode(code: string, options?: StripConsoleOptions): StripConsoleResult;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* @file constants.ts
|
|
54
|
+
* @description Global constants used across the project.
|
|
55
|
+
*
|
|
56
|
+
* Centralizing magic strings and default values here prevents typos
|
|
57
|
+
* and makes it trivial to change defaults in one place.
|
|
58
|
+
*/
|
|
59
|
+
/** The package name — used in logs, banners, and plugin names. */
|
|
60
|
+
declare const PACKAGE_NAME = "console-sniper";
|
|
61
|
+
/** Current version — ideally sync'd with package.json at build time. */
|
|
62
|
+
declare const PACKAGE_VERSION = "1.0.0";
|
|
63
|
+
/**
|
|
64
|
+
* Default console methods that are removed when `methods` is not specified.
|
|
65
|
+
*
|
|
66
|
+
* Extending this list is the primary way to support new console variants
|
|
67
|
+
* like `console.table`, `console.time`, `console.group`, etc.
|
|
68
|
+
*/
|
|
69
|
+
declare const DEFAULT_METHODS: readonly string[];
|
|
70
|
+
/**
|
|
71
|
+
* File extensions that the Babel parser can handle.
|
|
72
|
+
* These are used by the CLI file scanner to filter which files to process.
|
|
73
|
+
*
|
|
74
|
+
* Note: The Babel parser handles TypeScript and JSX natively via plugins.
|
|
75
|
+
*/
|
|
76
|
+
declare const SUPPORTED_EXTENSIONS: readonly string[];
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @file utils.ts
|
|
80
|
+
* @description Pure utility functions shared across the core engine.
|
|
81
|
+
*/
|
|
82
|
+
|
|
83
|
+
declare function resolveOptionsSync(options?: StripConsoleOptions): StripConsoleOptions & {
|
|
84
|
+
methods: string[];
|
|
85
|
+
removeComments: boolean;
|
|
86
|
+
silent: boolean;
|
|
87
|
+
};
|
|
88
|
+
declare function isSupportedFile(filePath: string): boolean;
|
|
89
|
+
declare function shouldProcessFile(filePath: string, include?: RegExp[], exclude?: RegExp[]): boolean;
|
|
90
|
+
declare function parseMethodList(input: string): string[];
|
|
91
|
+
declare function formatCount(count: number, noun: string): string;
|
|
92
|
+
|
|
93
|
+
export { DEFAULT_METHODS, PACKAGE_NAME, PACKAGE_VERSION, SUPPORTED_EXTENSIONS, StripConsoleOptions, StripConsoleResult, formatCount, isSupportedFile, parseMethodList, resolveOptionsSync, shouldProcessFile, stripConsoleFromCode };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
// src/core/stripConsole.ts
|
|
2
|
+
import { parse } from "@babel/parser";
|
|
3
|
+
import _traverse from "@babel/traverse";
|
|
4
|
+
import _generate from "@babel/generator";
|
|
5
|
+
import * as t from "@babel/types";
|
|
6
|
+
|
|
7
|
+
// src/core/constants.ts
|
|
8
|
+
var PACKAGE_NAME = "console-sniper";
|
|
9
|
+
var PACKAGE_VERSION = "1.0.0";
|
|
10
|
+
var DEFAULT_METHODS = [
|
|
11
|
+
"log",
|
|
12
|
+
"warn",
|
|
13
|
+
"error",
|
|
14
|
+
"info",
|
|
15
|
+
"debug"
|
|
16
|
+
];
|
|
17
|
+
var DEFAULT_REMOVE_COMMENTS = true;
|
|
18
|
+
var SUPPORTED_EXTENSIONS = [
|
|
19
|
+
".js",
|
|
20
|
+
".mjs",
|
|
21
|
+
".cjs",
|
|
22
|
+
".jsx",
|
|
23
|
+
".ts",
|
|
24
|
+
".tsx",
|
|
25
|
+
".mts",
|
|
26
|
+
".cts"
|
|
27
|
+
];
|
|
28
|
+
var CONSOLE_KEYWORD = "console";
|
|
29
|
+
|
|
30
|
+
// src/core/utils.ts
|
|
31
|
+
import path from "path";
|
|
32
|
+
function resolveOptionsSync(options) {
|
|
33
|
+
return {
|
|
34
|
+
methods: options?.methods?.length ? options.methods : [...DEFAULT_METHODS],
|
|
35
|
+
removeComments: options?.removeComments ?? DEFAULT_REMOVE_COMMENTS,
|
|
36
|
+
include: options?.include ?? [],
|
|
37
|
+
exclude: options?.exclude ?? [],
|
|
38
|
+
silent: options?.silent ?? false
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function isSupportedFile(filePath) {
|
|
42
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
43
|
+
return SUPPORTED_EXTENSIONS.includes(ext);
|
|
44
|
+
}
|
|
45
|
+
function shouldProcessFile(filePath, include = [], exclude = []) {
|
|
46
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
47
|
+
if (include.length > 0 && !include.some((p) => p.test(normalized))) return false;
|
|
48
|
+
if (exclude.length > 0 && exclude.some((p) => p.test(normalized))) return false;
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
function parseMethodList(input) {
|
|
52
|
+
return input.split(",").map((m) => m.trim()).filter((m) => m.length > 0);
|
|
53
|
+
}
|
|
54
|
+
function formatCount(count, noun) {
|
|
55
|
+
return `${count} ${noun}${count !== 1 ? "s" : ""}`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// src/core/stripConsole.ts
|
|
59
|
+
var traverse = typeof _traverse === "function" ? _traverse : _traverse.default;
|
|
60
|
+
var generate = typeof _generate === "function" ? _generate : _generate.default;
|
|
61
|
+
function stripConsoleFromCode(code, options) {
|
|
62
|
+
const opts = resolveOptionsSync(options);
|
|
63
|
+
const methodsToRemove = new Set(opts.methods);
|
|
64
|
+
const removedMethods = [];
|
|
65
|
+
const parserOptions = {
|
|
66
|
+
sourceType: "module",
|
|
67
|
+
// Support import/export statements
|
|
68
|
+
strictMode: false,
|
|
69
|
+
// Don't throw on sloppy-mode code
|
|
70
|
+
allowImportExportEverywhere: true,
|
|
71
|
+
allowReturnOutsideFunction: true,
|
|
72
|
+
allowSuperOutsideMethod: true,
|
|
73
|
+
plugins: [
|
|
74
|
+
"typescript",
|
|
75
|
+
// TypeScript syntax (types, generics, decorators)
|
|
76
|
+
"jsx",
|
|
77
|
+
// JSX/TSX syntax
|
|
78
|
+
"decorators",
|
|
79
|
+
// @decorator syntax
|
|
80
|
+
"classProperties",
|
|
81
|
+
"classPrivateProperties",
|
|
82
|
+
"classPrivateMethods",
|
|
83
|
+
"classStaticBlock",
|
|
84
|
+
"dynamicImport",
|
|
85
|
+
// import(...)
|
|
86
|
+
"exportDefaultFrom",
|
|
87
|
+
"exportNamespaceFrom",
|
|
88
|
+
"nullishCoalescingOperator",
|
|
89
|
+
// ??
|
|
90
|
+
"optionalChaining",
|
|
91
|
+
// ?.
|
|
92
|
+
"optionalCatchBinding",
|
|
93
|
+
"logicalAssignment",
|
|
94
|
+
// &&=, ||=, ??=
|
|
95
|
+
"numericSeparator",
|
|
96
|
+
// 1_000_000
|
|
97
|
+
"bigInt",
|
|
98
|
+
"importMeta",
|
|
99
|
+
// import.meta
|
|
100
|
+
"topLevelAwait"
|
|
101
|
+
]
|
|
102
|
+
};
|
|
103
|
+
let ast;
|
|
104
|
+
try {
|
|
105
|
+
ast = parse(code, parserOptions);
|
|
106
|
+
} catch (parseError) {
|
|
107
|
+
if (!opts.silent) {
|
|
108
|
+
console.warn(
|
|
109
|
+
`[console-sniper] Failed to parse code, skipping. Error: ${parseError instanceof Error ? parseError.message : String(parseError)}`
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
code,
|
|
114
|
+
removedCount: 0,
|
|
115
|
+
removedMethods: [],
|
|
116
|
+
changed: false
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
traverse(ast, {
|
|
120
|
+
ExpressionStatement(nodePath) {
|
|
121
|
+
const { expression } = nodePath.node;
|
|
122
|
+
if (!t.isCallExpression(expression)) return;
|
|
123
|
+
const { callee } = expression;
|
|
124
|
+
if (!t.isMemberExpression(callee)) return;
|
|
125
|
+
const { object, property } = callee;
|
|
126
|
+
if (!t.isIdentifier(object, { name: "console" })) return;
|
|
127
|
+
let methodName;
|
|
128
|
+
if (t.isIdentifier(property)) {
|
|
129
|
+
methodName = property.name;
|
|
130
|
+
} else if (t.isStringLiteral(property)) {
|
|
131
|
+
methodName = property.value;
|
|
132
|
+
}
|
|
133
|
+
if (!methodName || !methodsToRemove.has(methodName)) return;
|
|
134
|
+
removedMethods.push(methodName);
|
|
135
|
+
nodePath.remove();
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
if (opts.removeComments) {
|
|
139
|
+
removeConsoleComments(ast);
|
|
140
|
+
}
|
|
141
|
+
const { code: transformedCode } = generate(
|
|
142
|
+
ast,
|
|
143
|
+
{
|
|
144
|
+
retainLines: false,
|
|
145
|
+
// Compact output; set true if you need line parity
|
|
146
|
+
compact: false,
|
|
147
|
+
// Keep human-readable whitespace
|
|
148
|
+
concise: false,
|
|
149
|
+
comments: true,
|
|
150
|
+
// Include non-console comments
|
|
151
|
+
jsescOption: {
|
|
152
|
+
minimal: true
|
|
153
|
+
// Don't over-escape unicode
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
code
|
|
157
|
+
// Original source (used for sourcemap generation)
|
|
158
|
+
);
|
|
159
|
+
const removedCount = removedMethods.length;
|
|
160
|
+
return {
|
|
161
|
+
code: transformedCode,
|
|
162
|
+
removedCount,
|
|
163
|
+
removedMethods,
|
|
164
|
+
changed: removedCount > 0
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function removeConsoleComments(ast) {
|
|
168
|
+
function filterComments(comments) {
|
|
169
|
+
if (!comments || comments.length === 0) return comments ?? null;
|
|
170
|
+
return comments.filter(
|
|
171
|
+
(comment) => !comment.value.includes(CONSOLE_KEYWORD)
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
traverse(ast, {
|
|
175
|
+
enter(nodePath) {
|
|
176
|
+
const { node } = nodePath;
|
|
177
|
+
if (node.leadingComments) {
|
|
178
|
+
const filtered = filterComments(node.leadingComments);
|
|
179
|
+
node.leadingComments = filtered;
|
|
180
|
+
}
|
|
181
|
+
if (node.trailingComments) {
|
|
182
|
+
const filtered = filterComments(node.trailingComments);
|
|
183
|
+
node.trailingComments = filtered;
|
|
184
|
+
}
|
|
185
|
+
if (node.innerComments) {
|
|
186
|
+
const filtered = filterComments(node.innerComments);
|
|
187
|
+
node.innerComments = filtered;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
export {
|
|
193
|
+
DEFAULT_METHODS,
|
|
194
|
+
PACKAGE_NAME,
|
|
195
|
+
PACKAGE_VERSION,
|
|
196
|
+
SUPPORTED_EXTENSIONS,
|
|
197
|
+
formatCount,
|
|
198
|
+
isSupportedFile,
|
|
199
|
+
parseMethodList,
|
|
200
|
+
resolveOptionsSync,
|
|
201
|
+
shouldProcessFile,
|
|
202
|
+
stripConsoleFromCode
|
|
203
|
+
};
|
|
204
|
+
//# sourceMappingURL=index.js.map
|