Skip to content
This repository was archived by the owner on Oct 20, 2022. It is now read-only.

Commit 5e94b8a

Browse files
committed
First commit
0 parents  commit 5e94b8a

File tree

8 files changed

+6128
-0
lines changed

8 files changed

+6128
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/node_modules

.prettierrc.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"semi": false,
3+
"singleQuote": true,
4+
"printWidth": 120
5+
}

index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const lineParser = require('./line-parser')
2+
3+
exports.parseRedirectsFormat = lineParser.parse

line-parser.js

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
const FULL_URL_MATCHER = /^(https?):\/\/(.+)$/
2+
const FORWARD_STATUS_MATCHER = /^2\d\d!?$/
3+
4+
let URLclass = null
5+
6+
function parseURL(url) {
7+
if (typeof window !== 'undefined' && window.URL) {
8+
return new window.URL(url)
9+
}
10+
11+
URLclass = URLclass || require('url')
12+
return URLclass.parse(url)
13+
}
14+
15+
class Result {
16+
constructor() {
17+
this.success = []
18+
this.errors = []
19+
}
20+
21+
addSuccess(redirect) {
22+
this.success.push(redirect)
23+
}
24+
25+
addError(idx, line, options) {
26+
const reason = options && options.reason
27+
this.errors.push({
28+
lineNum: idx + 1,
29+
line,
30+
reason
31+
})
32+
}
33+
}
34+
35+
function parseFullOrigin(origin) {
36+
let url = null
37+
try {
38+
url = parseURL(origin)
39+
} catch (e) {
40+
return null
41+
}
42+
43+
return { host: url.host, scheme: url.protocol.replace(/:$/, ''), path: url.path }
44+
}
45+
46+
function splatForwardRule(redirect, nextPart) {
47+
return redirect.path.match(/\/\*$/) && nextPart.match(FORWARD_STATUS_MATCHER)
48+
}
49+
50+
function arrayToObj(source) {
51+
return source.reduce((obj, condition) => {
52+
if (condition == null) {
53+
return obj
54+
}
55+
const pair = condition.split('=')
56+
obj[pair[0]] = pair[1]
57+
return obj
58+
}, {})
59+
}
60+
61+
function parseStatus(source) {
62+
if (source == null) {
63+
return null
64+
}
65+
66+
return [parseInt(source, 10), source.match(/\!$/)]
67+
}
68+
69+
function redirectMatch(line) {
70+
const allParts = line.split(/\s+/).map(el => el.trim())
71+
let parts = []
72+
for (const i in allParts) {
73+
if (allParts[i].match(/^#/)) {
74+
break
75+
}
76+
parts.push(allParts[i])
77+
}
78+
79+
const origin = parts.shift()
80+
const redirect = origin.match(FULL_URL_MATCHER) ? parseFullOrigin(origin) : { path: origin }
81+
if (redirect == null || !parts.length) {
82+
return null
83+
}
84+
85+
if (splatForwardRule(redirect, parts[0])) {
86+
redirect.to = redirect.path.replace(/\/\*$/, '/:splat')
87+
} else {
88+
const newHostRuleIdx = parts.findIndex(el => el.match(/^\//) || el.match(FULL_URL_MATCHER))
89+
if (newHostRuleIdx < 0) {
90+
return null
91+
}
92+
93+
redirect.to = parts[newHostRuleIdx]
94+
if (newHostRuleIdx > 0) {
95+
redirect.params = arrayToObj(parts.slice(0, newHostRuleIdx))
96+
}
97+
98+
// remove parsed parts for params and host
99+
parts = parts.slice(newHostRuleIdx + 1)
100+
}
101+
102+
if (parts.length === 0) {
103+
return redirect
104+
}
105+
106+
const statusResult = parseStatus(parts.shift())
107+
if (statusResult) {
108+
redirect.status = statusResult[0]
109+
if (statusResult[1]) {
110+
redirect.force = true
111+
}
112+
}
113+
114+
if (parts.length) {
115+
const kv = arrayToObj(parts)
116+
if (kv.Sign) {
117+
redirect.signed = kv.Sign
118+
delete kv.Sign
119+
}
120+
if (Object.keys(kv).length) {
121+
redirect.conditions = kv
122+
}
123+
}
124+
125+
return redirect
126+
}
127+
128+
function isInvalidSource(redirect) {
129+
return redirect.path.match(/^\/\.netlify/)
130+
}
131+
132+
function isProxy(redirect) {
133+
return redirect.proxy || (redirect.to.match(/^https?:\/\//) && redirect.status === 200)
134+
}
135+
136+
function parse(text) {
137+
const result = new Result()
138+
139+
text.split('\n').forEach((line, idx) => {
140+
line = line.trim()
141+
if (line == '' || line.match(/^#/)) {
142+
return
143+
}
144+
145+
const redirect = redirectMatch(line)
146+
if (!redirect) {
147+
result.addError(idx, line)
148+
return
149+
}
150+
151+
if (isInvalidSource(redirect)) {
152+
result.addError(idx, line, { reason: 'Invalid /.netlify path in redirect source' })
153+
return
154+
}
155+
156+
if (isProxy(redirect)) {
157+
redirect.proxy = true
158+
}
159+
160+
result.addSuccess(redirect)
161+
})
162+
163+
return result
164+
}
165+
166+
exports.parse = parse

0 commit comments

Comments
 (0)