Skip to content

Commit 616d098

Browse files
committed
poc: preserve comments in esbuild
1 parent 954f504 commit 616d098

File tree

4 files changed

+132
-2
lines changed

4 files changed

+132
-2
lines changed

injected/scripts/utils/build.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { readFileSync } from 'fs';
33
import { cwd } from '../../../scripts/script-utils.js';
44
import { join } from 'path';
55
import * as esbuild from 'esbuild';
6+
import { commentPlugin } from './comment-plugin.js';
67
const ROOT = join(cwd(import.meta.url), '..', '..');
78
const DEBUG = false;
89

@@ -38,6 +39,7 @@ export async function bundle(params) {
3839
format: 'iife',
3940
bundle: true,
4041
metafile: true,
42+
legalComments: 'inline',
4143
globalName: name,
4244
loader: {
4345
'.css': 'text',
@@ -48,7 +50,7 @@ export async function bundle(params) {
4850
'import.meta.injectName': JSON.stringify(platform),
4951
'import.meta.trackerLookup': trackerLookup,
5052
},
51-
plugins: [loadFeaturesPlugin],
53+
plugins: [loadFeaturesPlugin, commentPlugin({ pathMatch: 'seedrandom/lib/alea', regex: /^\/\/ Copyright \(C\)/ })],
5254
banner: {
5355
js: prefixMessage,
5456
},
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { join } from 'path';
2+
import { promises } from 'node:fs';
3+
4+
/**
5+
* @param {object} params
6+
* @param {string} params.pathMatch
7+
* @param {RegExp} params.regex
8+
* @returns {import("esbuild").Plugin}
9+
*/
10+
export function commentPlugin({ pathMatch, regex }) {
11+
const PLUGIN_ID = 'comment-override';
12+
13+
/** @type {import("esbuild").Plugin} */
14+
const plugin = {
15+
name: PLUGIN_ID,
16+
setup(build) {
17+
// watch every import/require and mark matches for processing
18+
build.onResolve({ filter: /.*/ }, (args) => {
19+
const path = join(args.resolveDir, args.path);
20+
if (!path.includes(pathMatch)) return undefined;
21+
22+
// mark this file for processing
23+
return {
24+
path: args.path,
25+
namespace: PLUGIN_ID,
26+
pluginData: {
27+
fullPath: join(args.resolveDir, args.path + '.js'),
28+
},
29+
};
30+
});
31+
32+
// this will only run for files matched above
33+
build.onLoad({ filter: /.*/, namespace: PLUGIN_ID }, async (args) => {
34+
const text = await promises.readFile(args.pluginData.fullPath, 'utf8');
35+
return {
36+
contents: convertToLegalComments(text.toString(), regex),
37+
loader: 'js',
38+
};
39+
});
40+
},
41+
};
42+
return plugin;
43+
}
44+
45+
/**
46+
* Detect the start of a particular comment and change the
47+
* lines to have the prefix `//!`
48+
*
49+
* When a line is detect
50+
*
51+
* @param {string} source
52+
* @param {RegExp} regex
53+
*/
54+
export function convertToLegalComments(source, regex) {
55+
const lines = [];
56+
57+
let insideCommentBlock = false;
58+
59+
for (const line of source.split('\n')) {
60+
if (insideCommentBlock) {
61+
if (!line.match(/^\/\//)) {
62+
insideCommentBlock = false;
63+
}
64+
} else {
65+
if (line.match(regex)) {
66+
insideCommentBlock = true;
67+
}
68+
}
69+
70+
// chose line treatment
71+
if (insideCommentBlock) {
72+
lines.push('//!' + line.slice(2));
73+
} else {
74+
lines.push(line);
75+
}
76+
}
77+
78+
return lines.join('\n');
79+
}

injected/unit-test/comment-plugin.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { convertToLegalComments } from '../scripts/utils/comment-plugin.js';
2+
3+
describe('convertToLegalComments', () => {
4+
it('should convert matching comments to legal comments', () => {
5+
const source = '// Copyright\n// All rights reserved\n// 2024';
6+
const regex = /\/\/\s*Copyright/;
7+
const expected = '//! Copyright\n//! All rights reserved\n//! 2024';
8+
9+
expect(convertToLegalComments(source, regex)).toBe(expected);
10+
});
11+
12+
it("should not convert comments that don't match the regex", () => {
13+
const source = '// Regular comment\n// Another comment';
14+
const regex = /\/\/\s*License/;
15+
16+
expect(convertToLegalComments(source, regex)).toBe(source);
17+
});
18+
19+
it('should stop converting when encountering non-comment line', () => {
20+
const source = '// Copyright\n// All rights reserved\ncode line\n// Regular comment';
21+
const regex = /\/\/\s*Copyright/;
22+
const expected = '//! Copyright\n//! All rights reserved\ncode line\n// Regular comment';
23+
24+
expect(convertToLegalComments(source, regex)).toBe(expected);
25+
});
26+
27+
it('should handle empty string input', () => {
28+
const regex = /\/\/\s*Copyright/;
29+
30+
expect(convertToLegalComments('', regex)).toBe('');
31+
});
32+
33+
it('should handle multiple comment blocks', () => {
34+
const source = '// Copyright\n// Notice\ncode\n// Copyright\n// Notice';
35+
const regex = /\/\/\s*Copyright/;
36+
const expected = '//! Copyright\n//! Notice\ncode\n//! Copyright\n//! Notice';
37+
38+
expect(convertToLegalComments(source, regex)).toBe(expected);
39+
});
40+
41+
it('should preserve leading spaces in comments', () => {
42+
const source = '// Copyright\n// All rights reserved';
43+
const regex = /\/\/\s*Copyright/;
44+
const expected = '//! Copyright\n//! All rights reserved';
45+
46+
expect(convertToLegalComments(source, regex)).toBe(expected);
47+
});
48+
});

injected/unit-test/verify-artifacts.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ const checks = {
2323
tests: [
2424
{ kind: 'maxFileSize', value: CSS_OUTPUT_SIZE },
2525
{ kind: 'containsString', text: '$TRACKER_LOOKUP$', includes: true },
26+
{ kind: 'containsString', text: 'Copyright (C) 2010 by Johannes Baagøe <[email protected]>', includes: true },
2627
],
2728
},
2829
firefox: {
@@ -34,7 +35,7 @@ const checks = {
3435
},
3536
integration: {
3637
file: join(BUILD, 'integration/contentScope.js'),
37-
tests: [{ kind: 'containsString', text: 'trackerLookup: ', includes: true }],
38+
tests: [{ kind: 'containsString', text: 'init_define_import_meta_trackerLookup = ', includes: true }],
3839
},
3940
windows: {
4041
file: join(BUILD, 'windows/contentScope.js'),

0 commit comments

Comments
 (0)