remove c style comments from text file. (javascript source, json file etc)
-
This module removes comments from source files such as
typescriptandjavascript.
It does not remove lines that are meaningful totypescript, such as@ts-ignoreand<reference types="node"/>,
but removes other single line comments and multi line comments. -
For other single line comment and multi line comment, it is possible to control which comment is removed by setting scan listener.
-
This module is much faster than the popular comment removal module.
-
gulp plugin is available.
-
web version is available.
-
new property
keepJsDoc(v3.3.11) -
regex flags strict check (v3.3.15)
- Even if it is not written as a regular expression in the text,
it will be detected if it is valid as a regex (this is by design).
- Even if it is not written as a regular expression in the text,
-
npm
$ npm install rm-cstyle-cmts --save-dev # shorthand $ npm i rm-cstyle-cmts -D -
yarn
$ yarn add rm-cstyle-cmts -D
-
remove mode
const rmc = require("rm-cstyle-cmts"); const removed = rmc("<source file content>");
-
walkthrough mode (walkthrough mode does things like statistics for source code comments by setting a scan listener.)
const rmc = require("rm-cstyle-cmts"); /** @type {Map<string, number>} */ const tagStatistics = new Map(); /** * Take statistics for jsDoc tag * * @param {object} context * @param {TScannerEventContext["event"]} context.event - currently EScannerEvent.(SingleLineComment | MultiLineComment) only * @param {TScannerEventContext["fragment"]} context.fragment comment body * @param {TScannerEventContext["offset"]} context.offset - fragment start offset from original source */ function handleScanEvent({ event, fragment, offset }) => { if (event === /*EScannerEvent.MultiLineComment*/1) { if (/^\/\*\*[^*]/.test(fragment)) { const re = /(?<=[\s\*{])@\w+(?=\s)/g; /** @type {RegExpExecArray} */ let m; while (m = re.exec(fragment)) { const tag = m[0]; let count = tagStatistics.get(tag) || 0; tagStatistics.set(tag, count + 1); } } } return true; } // At current implementation, accept one listener only rmc.setListener(handleScanEvent); rmc.walk("<source file content>");
node module.
const rmc = require("rm-cstyle-cmts");const opt = { collectRegex: true, showErrorMessage: true, preserveBlanks: true };
const removed = rmc("<source file content>", opt);- source
{string}-- The target source content. - opt
{object}- opt.collectRegex
{true | undefined}-- Whether collect detected regex. Default:undefined. - opt.showErrorMessage
{true | undefined}-- Whether to display an error message. Default:undefined. - opt.preserveBlanks
{true | undefined}-- Whether preserve whitespace and blank lines. Default:undefined. - opt.path
{string | undefined}-- Optional file path for regex detection details. Default:undefined.
- opt.collectRegex
const opt = { collectRegex: true, showErrorMessage: true };
rmc.walk("<source file content>", opt);- source
{string}-- The target source content. - opt
{object}- opt.collectRegex
{true | undefined}-- Whether collect detected regex. Default:undefined. - opt.showErrorMessage
{true | undefined}-- Whether to display an error message. Default:undefined. - opt.path
{string | undefined}-- Optional file path for regex detection details. Default:undefined.
- opt.collectRegex
When collectRegex: true is enabled, you can inspect detected regex literals via rmc.getDetectedReContext().
If you also provide opt.path, each detection includes file path and position information.
const fs = require("fs");
const rmc = require("rm-cstyle-cmts");
const path = "src/demo.js";
const opt = { collectRegex: true, path };
const result = rmc(fs.readFileSync(path, "utf8"), opt);
const { detectedReLiterals, uniqReLiterals } = rmc.getDetectedReContext();
// detectedReLiterals entries are:
// - string (legacy format, when opt.path is not provided)
// - [path, "line:x,column:y", "/.../flags"] (when opt.path is provided)
detectedReLiterals.forEach((item) => {
if (typeof item === "string") {
console.log("regex:", item);
return;
}
const [filePath, position, regex] = item;
console.log("regex:", regex, "at", position, "in", filePath);
});
// uniqReLiterals is the unique list of regex literal strings.
console.log("uniq:", uniqReLiterals);
// NOTE: Reset this context between independent file validations.
rmc.reset();$ npm run benchmark-
We are comparing the process of deleting only
commentfrom the following sampleDetails
const json = `// see: http://json.schemastore.org/tsconfig { "compilerOptions": { /* Basic Options */ "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */ "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ // "allowJs": true, /* Allow javascript files to be compiled. */ // "checkJs": true, /* Report errors in .js files. */ // "declaration": true, /* Generates corresponding '.d.ts' file. */ // "sourceMap": true, /* Generates corresponding '.map' file. */ // "outFile": "./", /* Concatenate and emit output to single file. */ // "outDir": "./", /* Redirect output structure to the directory. */ /* Strict Type-Checking Options */ "strict": true, /* Enable all strict type-checking options. */ // "strictNullChecks": true, /* Enable strict null checks. */ // "strictFunctionTypes": true, /* Enable strict checking of function types. */ /* Additional Checks */ // "noUnusedLocals": true, /* Report errors on unused locals. */ // "noUnusedParameters": true, /* Report errors on unused parameters. */ /* Module Resolution Options */ // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ // "typeRoots": [], /* List of folders to include type definitions from. */ "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ /* Experimental Options */ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ }, "files": [ "y" ] }`;
yarn run v1.22.22
$ node bench.mjs
Platform info:
Windows_NT 10.0.19045 x64
Node.JS: 22.20.0
V8 : 12.4.254.21-node.33
Intel(R) Core(TM) i7-3770 CPU @ 3.40GHz x 8
strip-comments x 11,020 ops/sec ±1.26% (88 runs sampled)
strip-json-comments x 15,837 ops/sec ±0.60% (95 runs sampled)
rm-cstyle-cmts x 210,011 ops/sec ±0.09% (97 runs sampled)
rm-cstyle-cmts (webpack) x 183,336 ops/sec ±3.17% (88 runs sampled)
rm-cstyle-cmts (umd) x 198,935 ops/sec ±0.40% (90 runs sampled)
- - done - -
all results are equals? true # see NOTE
Done in 27.84s.yarn run v1.22.22
$ node bench.mjs
Platform info:
Windows_NT 10.0.19045 x64
Node.JS: 25.2.1
V8 : 14.1.146.11-node.14
Intel(R) Core(TM) i7-3770 CPU @ 3.40GHz x 8
strip-comments x 11,544 ops/sec ±0.25% (92 runs sampled)
strip-json-comments x 16,512 ops/sec ±0.18% (95 runs sampled)
rm-cstyle-cmts x 208,505 ops/sec ±0.58% (92 runs sampled)
rm-cstyle-cmts (webpack) x 215,666 ops/sec ±0.37% (90 runs sampled)
rm-cstyle-cmts (umd) x 214,950 ops/sec ±0.89% (90 runs sampled)
- - done - -
all results are equals? true # see NOTE
Done in 27.53s.- NOTE:
strip-commentsmay be buggy and is excluded from comparison
Walkthrough mode does not modify original source.
The current implementation calls the listener on line comment and multi-line comment scan events.
import * as rmc from "rm-cstyle-cmts";
/**
* @param {object} context
* @param {TScannerEventContext["event"]} context.event - currently EScannerEvent.(SingleLineComment | MultiLineComment) only
* @param {TScannerEventContext["fragment"]} context.fragment comment body
* @param {TScannerEventContext["offset"]} context.offset - fragment start offset from original source
*/
function handleScanEvent({ event, fragment, offset }) => {
if (event === /*EScannerEvent.MultiLineComment*/1) {
// remove mode: preserve JSDoc comment
// walkthrough mode: whether proceed walkthrough
return /^\/\*\*[^*]/.test(fragment);
}
return false;
}
// At current implementation, accept one listener only
rmc.setListener(handleScanEvent);
const opt = { collectRegex: true, showErrorMessage: true };
rmc("<commented source>", opt);
rmc.walk("<commented source>", opt);NOTE: Handling of listener in
remove modeandwalkthrough mode
-
In
walkthrough mode: Returnstrueto continue processing. otherwise stop processing. -
In
remove mode: it returnstrueto preserve in the line comment and multi line comment deletion process.
- sample task to scan javascript related files directly under
node_modules
$ npm run grmc-test:cjsconst gulp = require("gulp");
const grmc = require("rm-cstyle-cmts/cjs/gulp/");
const grmcOpt = {
preserveBlanks: undefined, // remove blank lines and trailing whitespace
renderProgress: true, // show progress
// isWalk: true // want walk through?
};
gulp.src(["./src/**/*.{js,jsx,ts,tsx}"]).pipe(
grmc.getTransformer(grmcOpt)
).pipe(gulp.dest("./tmp"));- preserveBlanks
{true | undefined}-- Whether preserve whitespace and blank lines. Default:undefined. - renderProgress
{true | undefined}-- log scan source path(relative) currently being processed. Default:undefined. - collectRegex
{true | undefined}-- Whether collect detected regex. Default:undefined. - isWalk
{true | undefined}-- Whether to run in walkthrough mode. Default:undefined. - extraExtensions
{string[] | undefined}-- Add additional extensions. Default:undefined. - disableDefaultExtensions
{true | undefined}-- useextraExtensionsonly, instead ofdefaultExtensions. Default:undefined.defaultExtensions- ".js", ".jsx", ".ts", ".tsx", ".cjs", ".mjs", ".cts", ".mts"
- timeMeasure
{true | undefined}-- Whether to record the processing time for each file (replace mode only). Default:undefined.
const gulp = require("gulp");
const grmc = require("rm-cstyle-cmts/cjs/gulp/");
// set scan event listener
grmc.getRmcInterface().setListener(({ event, fragment, offset }) => {
if (event === /*EScannerEvent.MultiLineComment*/1) {
// true: Concatenate fragment to result in "remove mode", continue walkthrough in "walkthrough mode".
// false: remove fragment in "remove mode", stop walkthrough in "walkthrough mode".
return /^\/\*(\*|!)\s/.test(fragment);
}
else if (event === /*EScannerEvent.SingleLineComment*/0) {
return /(?:\/\/\/?\s+@ts-\w+|\/\/\/\s*<reference)/.test(fragment);
}
// remove fragment in "remove mode", stop walkthrough in "walkthrough mode".
return false;
});
gulp.src(["./src/**/*.{js,jsx,ts,tsx}"]).pipe(
/**
* preserveBlanks : keep blank line and whitespaces, default is `undefined`.
*/
grmc.getTransformer({
preserveBlanks: undefined, // remove blank lines and trailing whitespace
renderProgress: true, // show progress
// isWalk: true // want walk through?
})
).pipe(gulp.dest("./tmp"));- You can use web version from cdn such as
jsdelivr.- can use the API through the
Rmcglobal variable.
- can use the API through the
<!-- or https://cdn.jsdelivr.net/npm/rm-cstyle-cmts@latest/umd/index.min.js -->
<script src="https://cdn.jsdelivr.net/npm/rm-cstyle-cmts@3/umd/index.min.js"></script>const source = `
/// <reference types="node"/>
import React from "react";
import ReactDOM from "react-dom";
/**
* jsdoc comment
*/
function App() {
return (
// TODO: can optionally include quote string in react syntax.
// such source input does not complete successfully.
<h1>Hello's world's</h1>
);
}
ReactDOM.render(<App />, document.getElementById("root"));
`;
/**
* You can use the API through the `Rmc` global variable.
*/
console.log(Rmc(source));
// print detected regex literals detail (in this case, nothing any result)
console.log(Rmc.getDetectedReContext());
// reset of related statistics
Rmc.reset();const rmc = require("rm-cstyle-cmts");
const input = " /** block comment */ const a = \"this is apple! \\n\", b = 'quoted \"strings\"', \
c = `list:\\n1. \${a}\\n2. \${b}\\n\${ /* comments */ ` - len: \${a.length + b.length}`}\\n ---`; \
/* . */ let i = 2, n = 12 / 4 * 7/i; // last coment. !";
const result = rmc(input);
console.log(result);
//> const a = "this is apple! \n", b = 'quoted "strings"', c = `list:\n1. ${a}\n2. ${b}\n${ /* comments */ ` - len: ${a.length + b.length}`}\n ---`; let i = 2, n = 12 / 4 * 7/i;const rmc = require("rm-cstyle-cmts");
const json = `// see: http://json.schemastore.org/tsconfig
{
"compilerOptions": {
/* Basic Options */
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */
// "allowJs": true, /* Allow javascript files to be compiled. */
// "checkJs": true, /* Report errors in .js files. */
// "declaration": true, /* Generates corresponding '.d.ts' file. */
// "sourceMap": true, /* Generates corresponding '.map' file. */
// "outFile": "./", /* Concatenate and emit output to single file. */
// "outDir": "./", /* Redirect output structure to the directory. */
/* Strict Type-Checking Options */
"strict": true, /* Enable all strict type-checking options. */
// "strictNullChecks": true, /* Enable strict null checks. */
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
/* Additional Checks */
// "noUnusedLocals": true, /* Report errors on unused locals. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
/* Module Resolution Options */
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
// "typeRoots": [], /* List of folders to include type definitions from. */
"esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
/* Experimental Options */
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
},
"files": [
"y"
]
}`;
const result = rmc(input);
console.log(result);
//> {
//> "compilerOptions": {
//> "target": "es5",
//> "module": "commonjs",
//> "strict": true,
//> "esModuleInterop": true
//> },
//> "files": [
//> "y"
//> ]
//> }