Skip to content

Commit

Permalink
feat: add an option to customize comment style
Browse files Browse the repository at this point in the history
Close #308
  • Loading branch information
mjeanroy committed Jul 31, 2019
1 parent c9e8643 commit dadf592
Show file tree
Hide file tree
Showing 4 changed files with 224 additions and 41 deletions.
17 changes: 15 additions & 2 deletions src/generate-block-comment.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,21 @@ const commenting = require('commenting');
* Generate block comment from given text content.
*
* @param {string} text Text content.
* @param {Object} commentStyle The comment style setting.
* @return {string} Block comment.
*/
module.exports = function generateBlockComment(text) {
return commenting(text.trim(), {extension: '.js'});
module.exports = function generateBlockComment(text, commentStyle) {
const options = {
extension: '.js',
};

if (commentStyle) {
options.style = new commenting.Style(
commentStyle.body,
commentStyle.start,
commentStyle.end
);
}

return commenting(text.trim(), options);
};
153 changes: 114 additions & 39 deletions src/license-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,38 @@ const OPTIONS = new Set([
'thirdParty',
]);

/**
* Pre-Defined comment style:
*
* - `regular` stands for "classic" block comment.
* - `ignored` stands for block comment starting with standard prefix ignored by minifier.
* - `slash` stands for "inline" style (i.e `//`).
* - `none` stands for no comment style at all.
*
* @type {Object<string, Object>}
*/
const COMMENT_STYLES = {
regular: {
start: '/**',
body: ' *',
end: ' */',
},

ignored: {
start: '/*!',
body: ' *',
end: ' */',
},

slash: {
start: '//',
body: '//',
end: '//',
},

none: null,
};

/**
* The plugin name.
* @type {string}
Expand Down Expand Up @@ -231,46 +263,10 @@ module.exports = class LicensePlugin {
const magicString = new MagicString(code);

const banner = this._options.banner;

let content;
if (_.isString(banner)) {
this.debug('prepend banner from template');
content = banner;
} else if (banner) {
const file = banner.file;
this.debug(`prepend banner from file: ${file}`);

const filePath = path.resolve(file);
const exists = fs.existsSync(filePath);
if (exists) {
const encoding = banner.encoding || 'utf-8';
this.debug(`use encoding: ${encoding}`);
content = fs.readFileSync(filePath, encoding);
} else {
this.debug('template file does not exist, skip.');
}
}

const content = this._readBanner(banner);
if (content) {
// Create the template function with lodash.
const tmpl = _.template(content);

// Generate the banner.
const pkg = this._pkg;
const dependencies = _.values(this._dependencies);
const data = banner.data ? _.result(banner, 'data') : {};

let text = tmpl({_, moment, pkg, dependencies, data});

// Make a block comment if needed
const trimmedBanner = text.trim();
const start = trimmedBanner.slice(0, 3);
if (start !== '/**' && start !== '/*!') {
text = generateBlockComment(text);
}

// Prepend the banner.
magicString.prepend(`${text}${EOL}`);
magicString.prepend(EOL);
magicString.prepend(this._generateBanner(content, banner));
}

const result = {
Expand Down Expand Up @@ -347,4 +343,83 @@ module.exports = class LicensePlugin {
console.log(`[${this.name}] -- ${msg}`);
}
}

/**
* Read banner from given options and returns it.
*
* @param {Object|string} banner Banner as a raw string, or banner options.
* @return {string} The banner template.
* @private
*/
_readBanner(banner) {
if (!banner) {
return '';
}

if (_.isString(banner)) {
this.debug('prepend banner from template');
return banner;
}

const file = banner.file;
this.debug(`prepend banner from file: ${file}`);

const filePath = path.resolve(file);
const exists = fs.existsSync(filePath);
if (!exists) {
this.debug('template file does not exist, skip.');
return '';
}

const encoding = banner.encoding || 'utf-8';
this.debug(`use encoding: ${encoding}`);
return fs.readFileSync(filePath, encoding);
}

/**
* Generate banner output from given raw string and given options.
*
* Banner output will be a JavaScript comment block, comment style may be customized using
* the `commentStyle` option.
*
* @param {string} content Banner content, as a raw string.
* @param {Object} banner Banner options.
* @return {string} The banner output.
* @private
*/
_generateBanner(content, banner) {
// Create the template function with lodash.
const tmpl = _.template(content);

// Generate the banner.
const pkg = this._pkg;
const dependencies = _.values(this._dependencies);
const data = banner.data ? _.result(banner, 'data') : {};
const text = tmpl({
_,
moment,
pkg,
dependencies,
data,
});

// Make a block comment if needed
const trimmedBanner = text.trim();
const start = trimmedBanner.slice(0, 3);
const startWithComment = start === '/**' || start === '/*!';

if (!startWithComment) {
const style = _.has(banner, 'commentStyle') ? banner.commentStyle : 'regular';
if (!_.has(COMMENT_STYLES, style)) {
throw new Error(`Unknown comment style ${style}, please use one of: ${_.keys(COMMENT_STYLES)}`);
}

const commentStyle = COMMENT_STYLES[style];
if (commentStyle) {
return generateBlockComment(text, commentStyle);
}
}

return text;
}
};
25 changes: 25 additions & 0 deletions test/generate-block-comment.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,29 @@ describe('generateBlockComment', () => {
'',
]));
});

it('should generate block comment from given text and override comment style', () => {
const text = join([
'First Line',
'',
'Second Line',
]);

const commentStyle = {
body: '//',
start: '//',
end: '//',
};

const comment = generateBlockComment(text, commentStyle);

expect(comment).toEqual(join([
'//',
'// First Line',
'//',
'// Second Line',
'//',
'',
]));
});
});
70 changes: 70 additions & 0 deletions test/license-plugin.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -591,6 +591,76 @@ describe('LicensePlugin', () => {
]));
});

it('should prepend banner and create block comment with a custom style', () => {
const instance = new LicensePlugin({
banner: {
file: path.join(__dirname, 'fixtures', 'banner.txt'),
commentStyle: 'ignored',
},
});

const code = 'var foo = 0;';

const result = instance.prependBanner(code);

expect(result).toBeDefined();
expect(result.code).toEqual(join([
'/*!',
' * Test banner.',
' *',
' * With a second line.',
' */',
'',
code,
]));
});

it('should prepend banner and create comment with slash style', () => {
const instance = new LicensePlugin({
banner: {
file: path.join(__dirname, 'fixtures', 'banner.txt'),
commentStyle: 'slash',
},
});

const code = 'var foo = 0;';

const result = instance.prependBanner(code);

expect(result).toBeDefined();
expect(result.code).toEqual(join([
'//',
'// Test banner.',
'//',
'// With a second line.',
'//',
'',
code,
]));
});

it('should prepend banner and create block comment without any style at all', () => {
const instance = new LicensePlugin({
banner: {
file: path.join(__dirname, 'fixtures', 'banner.txt'),
commentStyle: 'none',
},
});

const code = 'var foo = 0;';

const result = instance.prependBanner(code);

expect(result).toBeDefined();
expect(result.code).toEqual(join([
'Test banner.',
'',
'With a second line.',
'',
code,
]));
});

it('should prepend banner to bundle and create sourceMap', () => {
const instance = new LicensePlugin({
banner: {
Expand Down

0 comments on commit dadf592

Please sign in to comment.