Skip to content

Commit 1ceee77

Browse files
committed
Rewrite URL parser to run in linear time
1 parent df2941b commit 1ceee77

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+4254
-4868
lines changed

README.md

Lines changed: 89 additions & 113 deletions
Large diffs are not rendered by default.

package-lock.json

Lines changed: 50 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "autolinker",
3-
"version": "3.16.2",
3+
"version": "4.0.0",
44
"description": "Utility to automatically link the URLs, email addresses, phone numbers, hashtags, and mentions (Twitter, Instagram) in a given block of text/HTML",
55
"main": "./dist/commonjs/index.js",
66
"typings": "./dist/commonjs/index.d.ts",
@@ -18,6 +18,7 @@
1818
"start": "webpack serve --open",
1919
"test": "npm-run-all test:unit test:integration",
2020
"test:unit": "node --require=ts-node/register node_modules/jasmine/bin/jasmine.js \"tests/**/*.spec.ts\"",
21+
"test:unit:debug": "node --inspect-brk --require=ts-node/register node_modules/jasmine/bin/jasmine.js \"tests/**/*.spec.ts\"",
2122
"test:integration": "ts-node scripts/test-integration.ts",
2223
"update-tld-regex": "ts-node scripts/update-tld-regex.ts",
2324
"prepare": "husky install"
@@ -46,6 +47,7 @@
4647
"devDependencies": {
4748
"@rollup/plugin-commonjs": "^21.0.0",
4849
"@rollup/plugin-node-resolve": "^13.1.3",
50+
"@types/cli-table": "^0.3.0",
4951
"@types/dedent": "^0.7.0",
5052
"@types/fs-extra": "^9.0.13",
5153
"@types/jasmine": "^3.10.3",
@@ -54,6 +56,7 @@
5456
"@types/node": "^16.11.26",
5557
"@types/webpack": "^5.28.0",
5658
"axios": "^0.26.0",
59+
"cli-table": "^0.3.11",
5760
"css-loader": "^6.6.0",
5861
"dedent": "^0.7.0",
5962
"fast-glob": "^3.2.11",

scripts/build.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,10 @@ const es2015OutputDir = './dist/es2015';
1515
fse.outputFileSync(
1616
`${pkgDir}/src/version.ts`,
1717
dedent`
18-
// Important: this file is generated from the 'build' script and should not be
19-
// edited directly
20-
export const version = '${pkg.version}';
21-
`,
18+
// Important: this file is generated from the 'build' script and should not be
19+
// edited directly
20+
export const version = '${pkg.version}';
21+
` + '\n',
2222
'utf-8'
2323
);
2424

@@ -119,15 +119,17 @@ async function checkMinifiedFileSize() {
119119
const sizeInKb = stats.size / 1000;
120120
const maxExpectedSizeInKb = 47;
121121

122+
console.log(`dist/autolinker.min.js size: ${sizeInKb}kb`);
123+
122124
if (sizeInKb > maxExpectedSizeInKb) {
123125
throw new Error(dedent`
124-
Minified file size of ${sizeInKb.toFixed(2)}kb is greater than max
125-
expected minified size of ${maxExpectedSizeInKb}kb
126-
127-
This check is to make sure that a dependency is not accidentally
128-
added which significantly inflates the size of Autolinker. If
129-
additions to the codebase have been made though and a higher size
130-
is expected, bump the 'maxExpectedSizeInKb' number in ${__filename}
131-
`);
126+
Minified file size of ${sizeInKb.toFixed(2)}kb is greater than max
127+
expected minified size of ${maxExpectedSizeInKb}kb
128+
129+
This check is to make sure that a dependency is not accidentally
130+
added which significantly inflates the size of Autolinker. If
131+
additions to the codebase have been made though and a higher size
132+
is expected, bump the 'maxExpectedSizeInKb' number in ${__filename}
133+
`);
132134
}
133135
}

scripts/test-integration.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ import { execSync } from './util/exec-sync';
2222
fse.copySync(testsSrcDir, testsOutputDir);
2323

2424
// Create a .tar.gz output file like the one that would be downloaded from npm
25-
// TODO: Was using 'yarn' - does npm have a --filename arg?
26-
//await exec( `./node_modules/.bin/yarn pack --filename ./.tmp/tests-integration/autolinker.tar.gz`, {
2725
execSync(`${pkgRoot}/node_modules/.bin/npm pack --pack-destination ${testsOutputDir}`, {
2826
cwd: pkgRoot,
2927
});

scripts/update-tld-regex.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,16 @@ export async function updateTldRegex() {
1616
responseType: 'text',
1717
});
1818

19-
const tldRegex = domainsToRegex(tldsFile.data);
20-
let outputFile = dedent`
21-
// NOTE: THIS IS A GENERATED FILE\n// To update with the latest TLD list, run \`npm run update-tld-regex\`\n\n
22-
${tldRegex}
23-
`;
24-
fse.writeFile('./src/matcher/tld-regex.ts', outputFile);
19+
const tldRegexStr = domainsToRegex(tldsFile.data);
20+
let outputFile =
21+
dedent`
22+
// NOTE: THIS IS A GENERATED FILE\n// To update with the latest TLD list, run \`npm run update-tld-regex\`
23+
24+
export const tldRegexStr = '(?:${tldRegexStr})';\n
25+
26+
export const tldRegex = new RegExp('^' + tldRegexStr + '$');
27+
` + '\n';
28+
await fse.outputFile('./src/parser/tld-regex.ts', outputFile);
2529
}
2630

2731
function domainsToRegex(contents: string): string {
@@ -33,8 +37,7 @@ function domainsToRegex(contents: string): string {
3337
.filter(s => !!s) // remove empty elements;
3438
.sort(compareLengthLongestFirst);
3539

36-
const domainsRegexStr = `export const tldRegex = /(?:${domains.join('|')})/;\n`;
37-
return domainsRegexStr;
40+
return domains.join('|');
3841
}
3942

4043
function notCommentLine(line: string): boolean {

src/anchor-tag-builder.ts

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { Match } from './match/match';
21
import { HtmlTag } from './html-tag';
32
import { TruncateConfigObj } from './autolinker';
43
import { truncateSmart } from './truncate/truncate-smart';
54
import { truncateMiddle } from './truncate/truncate-middle';
65
import { truncateEnd } from './truncate/truncate-end';
6+
import { AbstractMatch } from './match/abstract-match';
77

88
/**
99
* @protected
@@ -64,11 +64,10 @@ export class AnchorTagBuilder {
6464
* Generates the actual anchor (<a>) tag to use in place of the
6565
* matched text, via its `match` object.
6666
*
67-
* @param {Autolinker.match.Match} match The Match instance to generate an
68-
* anchor tag from.
69-
* @return {Autolinker.HtmlTag} The HtmlTag instance for the anchor tag.
67+
* @param match The Match instance to generate an anchor tag from.
68+
* @return The HtmlTag instance for the anchor tag.
7069
*/
71-
build(match: Match) {
70+
public build(match: AbstractMatch) {
7271
return new HtmlTag({
7372
tagName: 'a',
7473
attrs: this.createAttrs(match),
@@ -81,11 +80,10 @@ export class AnchorTagBuilder {
8180
* tag being generated.
8281
*
8382
* @protected
84-
* @param {Autolinker.match.Match} match The Match instance to generate an
85-
* anchor tag from.
86-
* @return {Object} A key/value Object (map) of the anchor tag's attributes.
83+
* @param match The Match instance to generate an anchor tag from.
84+
* @return A key/value Object (map) of the anchor tag's attributes.
8785
*/
88-
protected createAttrs(match: Match) {
86+
protected createAttrs(match: AbstractMatch) {
8987
let attrs: { [attrName: string]: string } = {
9088
href: match.getAnchorHref(), // we'll always have the `href` attribute
9189
};
@@ -122,13 +120,13 @@ export class AnchorTagBuilder {
122120
* - "myLink myLink-mention myLink-twitter" // mention match with Twitter service
123121
*
124122
* @protected
125-
* @param {Autolinker.match.Match} match The Match instance to generate an
123+
* @param match The Match instance to generate an
126124
* anchor tag from.
127-
* @return {String} The CSS class string for the link. Example return:
125+
* @return The CSS class string for the link. Example return:
128126
* "myLink myLink-url". If no {@link #className} was configured, returns
129127
* an empty string.
130128
*/
131-
protected createCssClass(match: Match) {
129+
protected createCssClass(match: AbstractMatch): string {
132130
let className = this.className;
133131

134132
if (!className) {
@@ -149,11 +147,11 @@ export class AnchorTagBuilder {
149147
* {@link #truncate} config.
150148
*
151149
* @private
152-
* @param {String} anchorText The anchor tag's text (i.e. what will be
150+
* @param anchorText The anchor tag's text (i.e. what will be
153151
* displayed).
154-
* @return {String} The processed `anchorText`.
152+
* @return The processed `anchorText`.
155153
*/
156-
private processAnchorText(anchorText: string) {
154+
private processAnchorText(anchorText: string): string {
157155
anchorText = this.doTruncate(anchorText);
158156

159157
return anchorText;
@@ -166,11 +164,11 @@ export class AnchorTagBuilder {
166164
* `location` property. See {@link #truncate} for details.
167165
*
168166
* @private
169-
* @param {String} anchorText The anchor tag's text (i.e. what will be
167+
* @param anchorText The anchor tag's text (i.e. what will be
170168
* displayed).
171-
* @return {String} The truncated anchor text.
169+
* @return The truncated anchor text.
172170
*/
173-
private doTruncate(anchorText: string) {
171+
private doTruncate(anchorText: string): string {
174172
let truncate = this.truncate;
175173
if (!truncate || !truncate.length) return anchorText;
176174

0 commit comments

Comments
 (0)