rescript-relay-router
Version:
A ReScript web router for RescriptRelay.
1,763 lines (1,652 loc) • 170 kB
JavaScript
import * as Fs from 'fs';
import * as Url from 'url';
import * as Path from 'path';
import Chokidar from 'chokidar';
import FastGlob from 'fast-glob';
import * as VscodeJsonrpc from 'vscode-jsonrpc';
import * as FastFuzzy from 'fast-fuzzy';
import * as JsoncParser$1 from 'jsonc-parser';
import * as LinesAndColumns from 'lines-and-columns';
import * as $$Crypto from 'crypto';
import * as Cosmiconfig from 'cosmiconfig';
import * as MessagesJs from 'vscode-jsonrpc/lib/messages.js';
import * as Os from 'os';
import * as Child_process from 'child_process';
import Chalk from 'chalk';
var $$Error$1 = "JsError";
/* No side effect */
// Generated by ReScript, PLEASE EDIT WITH CARE
function $$delete$1(dict, string) {
delete(dict[string]);
}
/* No side effect */
function some(x) {
if (x === undefined) {
return {
BS_PRIVATE_NESTED_SOME_NONE: 0
};
} else if (x !== null && x.BS_PRIVATE_NESTED_SOME_NONE !== undefined) {
return {
BS_PRIVATE_NESTED_SOME_NONE: x.BS_PRIVATE_NESTED_SOME_NONE + 1 | 0
};
} else {
return x;
}
}
function nullable_to_opt(x) {
if (x == null) {
return ;
} else {
return some(x);
}
}
function valFromOption(x) {
if (!(x !== null && x.BS_PRIVATE_NESTED_SOME_NONE !== undefined)) {
return x;
}
var depth = x.BS_PRIVATE_NESTED_SOME_NONE;
if (depth === 0) {
return ;
} else {
return {
BS_PRIVATE_NESTED_SOME_NONE: depth - 1 | 0
};
}
}
/* No side effect */
// Generated by ReScript, PLEASE EDIT WITH CARE
function reduce(arr, init, f) {
return arr.reduce(f, init);
}
function filterMap$1(a, f) {
var l = a.length;
var r = new Array(l);
var j = 0;
for(var i = 0; i < l; ++i){
var v = a[i];
var v$1 = f(v);
if (v$1 !== undefined) {
r[j] = valFromOption(v$1);
j = j + 1 | 0;
}
}
r.length = j;
return r;
}
/* No side effect */
// Generated by ReScript, PLEASE EDIT WITH CARE
function makeDiagnostic(range, message) {
return {
range: range,
message: message,
source: "RescriptRelayRouter"
};
}
function makeHover(message, loc) {
return {
contents: {
kind: "markdown",
value: message
},
range: loc
};
}
function makeCompletionItem(label, kind) {
return {
label: label,
kind: kind
};
}
function codeActionKindToString(kind) {
switch (kind) {
case "Empty" :
return "";
case "QuickFix" :
return "quickfix";
case "Refactor" :
return "refactor";
case "RefactorExtract" :
return "refactor.extract";
case "RefactorInline" :
return "refactor.inline";
case "RefactorRewrite" :
return "refactor.rewrite";
case "Source" :
return "source";
case "SourceOrganizeImports" :
return "source.organizeImports";
case "SourceFixAll" :
return "source.fixAll";
}
}
function make$6(uri, overwrite, ignoreIfExists) {
return {
kind: "create",
uri: uri,
options: {
overwrite: overwrite,
ignoreIfExists: ignoreIfExists
}
};
}
var CreateFile = {
make: make$6
};
function make$1$2(textDocumentUri, edits) {
return {
textDocument: {
uri: textDocumentUri
},
edits: edits
};
}
var TextDocumentEdit = {
make: make$1$2
};
var DocumentChange = {
CreateFile: CreateFile,
TextDocumentEdit: TextDocumentEdit
};
function makeOpenFileCommand(title, fileUri) {
return {
title: title,
command: "vscode.open",
arguments: [fileUri]
};
}
function makeOpenFileAtPosCommand(title, fileUri, pos) {
return {
title: title,
command: "vscode-rescript-relay.open-pos-in-doc",
arguments: [
fileUri,
pos.line.toString(),
pos.character.toString()
]
};
}
function makeOpenRouteDefinitionsCommand(title, routes) {
return {
title: title,
command: "vscode-rescript-relay.open-route-definitions",
arguments: routes.map(function (r) {
return r.sourceFilePath + ";" + r.routeName + ";" + r.loc.line.toString() + ";" + r.loc.character.toString() + ";" + r.routeRendererFilePath;
})
};
}
function makeTextOnlyCommand(title) {
return {
title: title,
command: "",
arguments: undefined
};
}
var Command = {
makeOpenFileCommand: makeOpenFileCommand,
makeOpenFileAtPosCommand: makeOpenFileAtPosCommand,
makeOpenRouteDefinitionsCommand: makeOpenRouteDefinitionsCommand,
makeTextOnlyCommand: makeTextOnlyCommand
};
function makeCodeLensItem(range, command) {
return {
range: range,
command: command
};
}
function makeDocumentLink(range, fileUri, tooltip) {
return {
range: range,
target: fileUri,
tooltip: tooltip
};
}
/* No side effect */
// Generated by ReScript, PLEASE EDIT WITH CARE
function forEach(opt, f) {
if (opt !== undefined) {
return f(valFromOption(opt));
}
}
function flatMap(opt, f) {
if (opt !== undefined) {
return f(valFromOption(opt));
}
}
function getOr(opt, $$default) {
if (opt !== undefined) {
return valFromOption(opt);
} else {
return $$default;
}
}
function isSome(x) {
return x !== undefined;
}
function isNone(x) {
return x === undefined;
}
/* No side effect */
var idMap = new Map();
function create(str) {
var v = idMap.get(str);
var id;
if (v !== undefined) {
var id$1 = v + 1 | 0;
idMap.set(str, id$1);
id = id$1;
} else {
idMap.set(str, 1);
id = 1;
}
return str + ("/" + id);
}
function is_extension(e) {
if (e == null) {
return false;
} else {
return typeof e.RE_EXN_ID === "string";
}
}
/* idMap Not a pure module */
function internalToOCamlException(e) {
if (is_extension(e)) {
return e;
} else {
return {
RE_EXN_ID: "JsError",
_1: e
};
}
}
/* Caml_exceptions Not a pure module */
// Generated by ReScript, PLEASE EDIT WITH CARE
function copyAuxCont(_cellX, _prec) {
while(true) {
var prec = _prec;
var cellX = _cellX;
if (!cellX) {
return prec;
}
var next = {
hd: cellX.hd,
tl: /* [] */0
};
prec.tl = next;
_prec = next;
_cellX = cellX.tl;
continue ;
}}
function copyAuxWitFilter(f, _cellX, _prec) {
while(true) {
var prec = _prec;
var cellX = _cellX;
if (!cellX) {
return ;
}
var t = cellX.tl;
var h = cellX.hd;
if (f(h)) {
var next = {
hd: h,
tl: /* [] */0
};
prec.tl = next;
_prec = next;
_cellX = t;
continue ;
}
_cellX = t;
continue ;
}}
function copyAuxWitFilterMap(f, _cellX, _prec) {
while(true) {
var prec = _prec;
var cellX = _cellX;
if (!cellX) {
return ;
}
var t = cellX.tl;
var h = f(cellX.hd);
if (h !== undefined) {
var next = {
hd: valFromOption(h),
tl: /* [] */0
};
prec.tl = next;
_prec = next;
_cellX = t;
continue ;
}
_cellX = t;
continue ;
}}
function copyAuxWithMap(_cellX, _prec, f) {
while(true) {
var prec = _prec;
var cellX = _cellX;
if (!cellX) {
return ;
}
var next = {
hd: f(cellX.hd),
tl: /* [] */0
};
prec.tl = next;
_prec = next;
_cellX = cellX.tl;
continue ;
}}
function concat(xs, ys) {
if (!xs) {
return ys;
}
var cell = {
hd: xs.hd,
tl: /* [] */0
};
copyAuxCont(xs.tl, cell).tl = ys;
return cell;
}
function map(xs, f) {
if (!xs) {
return /* [] */0;
}
var cell = {
hd: f(xs.hd),
tl: /* [] */0
};
copyAuxWithMap(xs.tl, cell, f);
return cell;
}
function length(xs) {
var _x = xs;
var _acc = 0;
while(true) {
var acc = _acc;
var x = _x;
if (!x) {
return acc;
}
_acc = acc + 1 | 0;
_x = x.tl;
continue ;
}}
function fillAux(arr, _i, _x) {
while(true) {
var x = _x;
var i = _i;
if (!x) {
return ;
}
arr[i] = x.hd;
_x = x.tl;
_i = i + 1 | 0;
continue ;
}}
function fromArray(a) {
var _i = a.length - 1 | 0;
var _res = /* [] */0;
while(true) {
var res = _res;
var i = _i;
if (i < 0) {
return res;
}
_res = {
hd: a[i],
tl: res
};
_i = i - 1 | 0;
continue ;
}}
function toArray(x) {
var len = length(x);
var arr = new Array(len);
fillAux(arr, 0, x);
return arr;
}
function reverseConcat(_l1, _l2) {
while(true) {
var l2 = _l2;
var l1 = _l1;
if (!l1) {
return l2;
}
_l2 = {
hd: l1.hd,
tl: l2
};
_l1 = l1.tl;
continue ;
}}
function reverse(l) {
return reverseConcat(l, /* [] */0);
}
function concatMany(xs) {
var len = xs.length;
if (len === 1) {
return xs[0];
}
if (len === 0) {
return /* [] */0;
}
var len$1 = xs.length;
var v = xs[len$1 - 1 | 0];
for(var i = len$1 - 2 | 0; i >= 0; --i){
v = concat(xs[i], v);
}
return v;
}
function has(_xs, x, eq) {
while(true) {
var xs = _xs;
if (!xs) {
return false;
}
if (eq(xs.hd, x)) {
return true;
}
_xs = xs.tl;
continue ;
}}
function find(_xs, p) {
while(true) {
var xs = _xs;
if (!xs) {
return ;
}
var x = xs.hd;
if (p(x)) {
return some(x);
}
_xs = xs.tl;
continue ;
}}
function filter(_xs, p) {
while(true) {
var xs = _xs;
if (!xs) {
return /* [] */0;
}
var t = xs.tl;
var h = xs.hd;
if (p(h)) {
var cell = {
hd: h,
tl: /* [] */0
};
copyAuxWitFilter(p, t, cell);
return cell;
}
_xs = t;
continue ;
}}
function filterMap(_xs, p) {
while(true) {
var xs = _xs;
if (!xs) {
return /* [] */0;
}
var t = xs.tl;
var h = p(xs.hd);
if (h !== undefined) {
var cell = {
hd: valFromOption(h),
tl: /* [] */0
};
copyAuxWitFilterMap(p, t, cell);
return cell;
}
_xs = t;
continue ;
}}
/* No side effect */
// Generated by ReScript, PLEASE EDIT WITH CARE
function make$5(routeNamePath, loc) {
return {
routeNamePath: routeNamePath,
loc: loc
};
}
function getRouteName(t) {
return getOr(toArray(t.routeNamePath).pop(), "");
}
function getFullRouteName(t) {
return toArray(t.routeNamePath).join("__");
}
function getFullRouteAccessPath(t) {
return toArray(t.routeNamePath).join(".") + ".Route";
}
function getRouteRendererName(t) {
return getFullRouteName(t) + "_route_renderer";
}
function getRouteRendererFileName(t) {
return getRouteRendererName(t) + ".res";
}
function toGeneratedRouteModuleName(t) {
return "Route__" + getFullRouteName(t) + "_route";
}
function getLoc(t) {
return t.loc;
}
var RouteName = {
make: make$5,
getRouteName: getRouteName,
getFullRouteName: getFullRouteName,
getFullRouteAccessPath: getFullRouteAccessPath,
getRouteRendererName: getRouteRendererName,
getRouteRendererFileName: getRouteRendererFileName,
toGeneratedRouteModuleName: toGeneratedRouteModuleName,
getLoc: getLoc
};
function cleanPathParamTypeAnnotations(p) {
if (p.includes(":", 1)) {
return ":" + getOr(p.slice(1).split(":")[0], "");
} else {
return p;
}
}
function make$1$1(path, currentRoutePath) {
var cleanPath = getOr(path.split("?")[0], "");
return {
pathSegment: cleanPath,
currentRoutePath: map(filter(concat(reverse(fromArray(cleanPath.split("/"))), currentRoutePath.currentRoutePath), (function (urlPart) {
return urlPart !== "";
})), (function (p) {
return cleanPathParamTypeAnnotations(p);
}))
};
}
function getPathSegment(t) {
return t.pathSegment.split("/").map(function (p) {
return cleanPathParamTypeAnnotations(p);
}).join("/");
}
function getFullRoutePath(t) {
return "/" + toArray(reverse(t.currentRoutePath)).join("/");
}
function toPattern(t) {
return "/" + toArray(filterMap(reverse(t.currentRoutePath), (function (part) {
if (part === "/") {
return ;
} else {
return part;
}
}))).join("/");
}
function empty() {
return {
pathSegment: "",
currentRoutePath: /* [] */0
};
}
var RoutePath = {
make: make$1$1,
getPathSegment: getPathSegment,
getFullRoutePath: getFullRoutePath,
toPattern: toPattern,
empty: empty
};
/* No side effect */
/**
MIT License
Copyright (c) React Training 2015-2019 Copyright (c) Remix Software 2020-2022
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
// RescriptRelayRouter note:
// Inlined + modified from https://github.com/remix-run/react-router/blob/main/packages/router/router.ts
/**
* Matches the given routes to a location and returns the match data.
*
* @see https://reactrouter.com/utils/match-routes
*/
function matchRoutes(routes, locationArg, basename = "/") {
let location =
typeof locationArg === "string" ? parsePath(locationArg) : locationArg;
let pathname = stripBasename(location.pathname || "/", basename);
if (pathname == null) {
return null;
}
let branches = flattenRoutes(routes);
rankRouteBranches(branches);
let matches = null;
for (let i = 0; matches == null && i < branches.length; ++i) {
matches = matchRouteBranch(
branches[i],
// Incoming pathnames are generally encoded from either window.location
// or from router.navigate, but we want to match against the unencoded
// paths in the route definitions. Memory router locations won't be
// encoded here but there also shouldn't be anything to decode so this
// should be a safe operation. This avoids needing matchRoutes to be
// history-aware.
safelyDecodeURI(pathname)
);
}
return matches;
}
function flattenRoutes(
routes,
branches = [],
parentsMeta = [],
parentPath = ""
) {
routes.forEach((route, index) => {
let meta = {
relativePath: route.path || "",
caseSensitive: route.caseSensitive === true,
childrenIndex: index,
route,
};
if (meta.relativePath.startsWith("/")) {
invariant(
meta.relativePath.startsWith(parentPath),
`Absolute route path "${meta.relativePath}" nested under path ` +
`"${parentPath}" is not valid. An absolute child route path ` +
`must start with the combined path of all its parent routes.`
);
meta.relativePath = meta.relativePath.slice(parentPath.length);
}
let path = joinPaths([parentPath, meta.relativePath]);
let routesMeta = parentsMeta.concat(meta);
// Add the children before adding this route to the array so we traverse the
// route tree depth-first and child routes appear before their parents in
// the "flattened" version.
if (route.children && route.children.length > 0) {
invariant(
// Our types know better, but runtime JS may not!
// @ts-expect-error
route.index !== true,
`Index routes must not have child routes. Please remove ` +
`all child routes from route path "${path}".`
);
flattenRoutes(route.children, branches, routesMeta, path);
}
// Routes without a path shouldn't ever match by themselves unless they are
// index routes, so don't add them to the list of possible branches.
if (route.path == null && !route.index) {
return;
}
branches.push({ path, score: computeScore(path, route.index), routesMeta });
});
return branches;
}
function rankRouteBranches(branches) {
branches.sort((a, b) =>
a.score !== b.score
? b.score - a.score // Higher score first
: compareIndexes(
a.routesMeta.map((meta) => meta.childrenIndex),
b.routesMeta.map((meta) => meta.childrenIndex)
)
);
}
const paramRe = /^:\w+$/;
const dynamicSegmentValue = 3;
const regexSegmentValue = 7;
const indexRouteValue = 2;
const emptySegmentValue = 1;
const staticSegmentValue = 10;
const splatPenalty = -2;
const isSplat = (s) => s === "*";
function computeScore(path, index) {
let segments = path.split("/");
let initialScore = segments.length;
if (segments.some(isSplat)) {
initialScore += splatPenalty;
}
if (index) {
initialScore += indexRouteValue;
}
return segments
.filter((s) => !isSplat(s))
.reduce((score, segment) => {
let scoreToAdd = 0;
if (paramRe.test(segment)) {
// Means regexp segment
if (segment.endsWith(")")) {
scoreToAdd = regexSegmentValue;
} else {
scoreToAdd = dynamicSegmentValue;
}
} else {
if (segment === "") {
scoreToAdd = emptySegmentValue;
} else {
scoreToAdd = staticSegmentValue;
}
}
return score + scoreToAdd;
}, initialScore);
}
function compareIndexes(a, b) {
let siblings =
a.length === b.length && a.slice(0, -1).every((n, i) => n === b[i]);
return siblings
? // If two routes are siblings, we should try to match the earlier sibling
// first. This allows people to have fine-grained control over the matching
// behavior by simply putting routes with identical paths in the order they
// want them tried.
a[a.length - 1] - b[b.length - 1]
: // Otherwise, it doesn't really make sense to rank non-siblings by index,
// so they sort equally.
0;
}
function matchRouteBranch(branch, pathname) {
let { routesMeta } = branch;
let matchedParams = {};
let matchedPathname = "/";
let matches = [];
for (let i = 0; i < routesMeta.length; ++i) {
let meta = routesMeta[i];
let end = i === routesMeta.length - 1;
let remainingPathname =
matchedPathname === "/"
? pathname
: pathname.slice(matchedPathname.length) || "/";
let match = matchPath(
{ path: meta.relativePath, caseSensitive: meta.caseSensitive, end },
remainingPathname
);
if (!match) return null;
Object.assign(matchedParams, match.params);
let route = meta.route;
matches.push({
// TODO: Can this as be avoided?
params: matchedParams,
pathname: joinPaths([matchedPathname, match.pathname]),
pathnameBase: normalizePathname(
joinPaths([matchedPathname, match.pathnameBase])
),
route,
});
if (match.pathnameBase !== "/") {
matchedPathname = joinPaths([matchedPathname, match.pathnameBase]);
}
}
return matches;
}
/**
* Performs pattern matching on a URL pathname and returns information about
* the match.
*
* @see https://reactrouter.com/utils/match-path
*/
function matchPath(pattern, pathname) {
if (typeof pattern === "string") {
pattern = { path: pattern, caseSensitive: false, end: true };
}
let [matcher, paramNames] = compilePath(
pattern.path,
pattern.caseSensitive,
pattern.end
);
let match = pathname.match(matcher);
if (!match) return null;
let matchedPathname = match[0];
let pathnameBase = matchedPathname.replace(/(.)\/+$/, "$1");
let captureGroups = match.slice(1);
let params = paramNames.reduce((memo, paramName, index) => {
// We need to compute the pathnameBase here using the raw splat value
// instead of using params["*"] later because it will be decoded then
if (paramName === "*") {
let splatValue = captureGroups[index] || "";
pathnameBase = matchedPathname
.slice(0, matchedPathname.length - splatValue.length)
.replace(/(.)\/+$/, "$1");
}
memo[paramName] = safelyDecodeURIComponent(
captureGroups[index] || "",
paramName
);
return memo;
}, {});
return {
params,
pathname: matchedPathname,
pathnameBase,
pattern,
};
}
function compilePath(path, caseSensitive = false, end = true) {
warning(
path === "*" || !path.endsWith("*") || path.endsWith("/*"),
`Route path "${path}" will be treated as if it were ` +
`"${path.replace(/\*$/, "/*")}" because the \`*\` character must ` +
`always follow a \`/\` in the pattern. To get rid of this warning, ` +
`please change the route path to "${path.replace(/\*$/, "/*")}".`
);
let paramNames = [];
let regexpSource =
"^" +
path
.replace(/\/*\*?$/, "") // Ignore trailing / and /*, we'll handle it below
.replace(/^\/*/, "/") // Make sure it has a leading /
.replace(/[\\.*+^$?{}[\]]/g, "\\$&") // Escape special regex chars
.replace(/:([\w(|)-]+)/g, (_, paramName) => {
// Check if this is a regexp param. If so, special treatment.
if (paramName.endsWith(")")) {
let [pname, regexp] = paramName.slice(0, -1).split("(");
paramNames.push(pname);
return `(${regexp})`;
}
paramNames.push(paramName);
return "([^\\/]+)";
});
if (path.endsWith("*")) {
paramNames.push("*");
regexpSource +=
path === "*" || path === "/*"
? "(.*)$" // Already matched the initial /, just match the rest
: "(?:\\/(.+)|\\/*)$"; // Don't include the / in params["*"]
} else if (end) {
// When matching to the end, ignore trailing slashes
regexpSource += "\\/*$";
} else if (path !== "" && path !== "/") {
// If our path is non-empty and contains anything beyond an initial slash,
// then we have _some_ form of path in our regex so we should expect to
// match only if we find the end of this path segment. Look for an optional
// non-captured trailing slash (to match a portion of the URL) or the end
// of the path (if we've matched to the end). We used to do this with a
// word boundary but that gives false positives on routes like
// /user-preferences since `-` counts as a word boundary.
regexpSource += "(?:(?=\\/|$))";
} else ;
let matcher = new RegExp(regexpSource, caseSensitive ? undefined : "i");
return [matcher, paramNames];
}
function safelyDecodeURI(value) {
try {
return decodeURI(value);
} catch (error) {
warning(
false,
`The URL path "${value}" could not be decoded because it is is a ` +
`malformed URL segment. This is probably due to a bad percent ` +
`encoding (${error}).`
);
return value;
}
}
function safelyDecodeURIComponent(value, paramName) {
try {
return decodeURIComponent(value);
} catch (error) {
warning(
false,
`The value for the URL param "${paramName}" will not be decoded because` +
` the string "${value}" is a malformed URL segment. This is probably` +
` due to a bad percent encoding (${error}).`
);
return value;
}
}
/**
* @private
*/
function stripBasename(pathname, basename) {
if (basename === "/") return pathname;
if (!pathname.toLowerCase().startsWith(basename.toLowerCase())) {
return null;
}
// We want to leave trailing slash behavior in the user's control, so if they
// specify a basename with a trailing slash, we should support it
let startIndex = basename.endsWith("/")
? basename.length - 1
: basename.length;
let nextChar = pathname.charAt(startIndex);
if (nextChar && nextChar !== "/") {
// pathname does not start with basename/
return null;
}
return pathname.slice(startIndex) || "/";
}
function invariant(value, message) {
if (value === false || value === null || typeof value === "undefined") {
throw new Error(message);
}
}
/**
* @private
*/
function warning(cond, message) {
if (!cond) {
// eslint-disable-next-line no-console
if (typeof console !== "undefined") console.warn(message);
try {
// Welcome to debugging React Router!
//
// This error is thrown as a convenience so you can more easily
// find the source for a warning that appears in the console by
// enabling "pause on exceptions" in your JavaScript debugger.
throw new Error(message);
// eslint-disable-next-line no-empty
} catch (e) {}
}
}
/**
* @private
*/
const joinPaths = (paths) => paths.join("/").replace(/\/\/+/g, "/");
/**
* @private
*/
const normalizePathname = (pathname) =>
pathname.replace(/\/+$/, "").replace(/^\/*/, "/");
// Generated by ReScript, PLEASE EDIT WITH CARE
function decodeParseErrorCode(code) {
switch (code) {
case 1 :
return "InvalidSymbol";
case 2 :
return "InvalidNumberFormat";
case 3 :
return "PropertyNameExpected";
case 4 :
return "ValueExpected";
case 5 :
return "ColonExpected";
case 6 :
return "CommaExpected";
case 7 :
return "CloseBraceExpected";
case 8 :
return "CloseBracketExpected";
case 9 :
return "EndOfFileExpected";
case 10 :
return "InvalidCommentToken";
case 11 :
return "UnexpectedEndOfComment";
case 12 :
return "UnexpectedEndOfString";
case 13 :
return "UnexpectedEndOfNumber";
case 14 :
return "InvalidUnicode";
case 15 :
return "InvalidEscapeCharacter";
case 16 :
return "InvalidCharacter";
default:
return ;
}
}
function nodeToString(node) {
switch (node.TAG) {
case "Object" :
return "object";
case "Array" :
return "array";
case "Boolean" :
return "boolean(" + (
node.value ? "true" : "false"
) + "})";
case "String" :
return "string(\"" + node.value + "\")";
case "Number" :
return "number(" + node.value.toString() + ")";
case "Null" :
return "null";
}
}
/* No side effect */
// Generated by ReScript, PLEASE EDIT WITH CARE
function pathInRoutesFolder$1(config, fileNameOpt) {
var fileName = fileNameOpt !== undefined ? fileNameOpt : "";
return Path.join(config.routesFolderPath, fileName);
}
function stringToQueryParam(str) {
if (str.startsWith("array<")) {
var arrayValue = str.replace("array<", "");
var arrayValue$1 = arrayValue.slice(0, arrayValue.length - 1 | 0);
var value = stringToQueryParam(arrayValue$1);
if (value.TAG === "Ok") {
return {
TAG: "Ok",
_0: {
TAG: "Array",
_0: value._0
}
};
} else {
return value;
}
}
switch (str) {
case "bool" :
return {
TAG: "Ok",
_0: "Boolean"
};
case "float" :
return {
TAG: "Ok",
_0: "Float"
};
case "int" :
return {
TAG: "Ok",
_0: "Int"
};
case "string" :
return {
TAG: "Ok",
_0: "String"
};
default:
var required = str.endsWith("!");
var maybeCustomModule = required ? str.replace("!", "") : str;
var firstChar = maybeCustomModule.charAt(0);
var correctEnding = maybeCustomModule.endsWith(".t");
if (!correctEnding) {
return {
TAG: "Error",
_0: undefined
};
}
var match = firstChar.toUpperCase();
if (firstChar === "" || firstChar !== match) {
return {
TAG: "Error",
_0: undefined
};
} else {
return {
TAG: "Ok",
_0: {
TAG: "CustomModule",
moduleName: maybeCustomModule.slice(0, maybeCustomModule.length - 2 | 0),
required: required
}
};
}
}
}
function rangeFromNode(node, lineLookup) {
return {
start: lineLookup.locationForIndex(node.offset),
end_: lineLookup.locationForIndex(node.offset + node.length | 0)
};
}
function transformNode(node, ctx) {
var loc = rangeFromNode(node, ctx.lineLookup);
var match = node.type;
var match$1 = node.value;
var match$2 = node.children;
if (match === "null") {
return {
TAG: "Null",
loc: loc,
error: undefined
};
}
if (match === "boolean") {
if (match$1 !== undefined && !(!Array.isArray(match$1) && (match$1 === null || typeof match$1 !== "object") && typeof match$1 !== "number" && typeof match$1 !== "string" && typeof match$1 !== "boolean" || typeof match$1 !== "boolean")) {
return {
TAG: "Boolean",
loc: loc,
error: undefined,
value: match$1
};
} else {
return ;
}
}
if (match === "string") {
if (match$1 !== undefined && !(!Array.isArray(match$1) && (match$1 === null || typeof match$1 !== "object") && typeof match$1 !== "number" && typeof match$1 !== "string" && typeof match$1 !== "boolean" || typeof match$1 !== "string")) {
return {
TAG: "String",
loc: loc,
error: undefined,
value: match$1
};
} else {
return ;
}
}
if (match !== "object") {
if (match === "number") {
if (match$1 !== undefined && !(!Array.isArray(match$1) && (match$1 === null || typeof match$1 !== "object") && typeof match$1 !== "number" && typeof match$1 !== "string" && typeof match$1 !== "boolean" || typeof match$1 !== "number")) {
return {
TAG: "Number",
loc: loc,
error: undefined,
value: match$1
};
} else {
return ;
}
} else if (match === "array" && match$2 !== undefined) {
return {
TAG: "Array",
loc: loc,
error: undefined,
children: filterMap$1(match$2, (function (child) {
return transformNode(child, ctx);
}))
};
} else {
return ;
}
}
if (match$2 === undefined) {
return ;
}
var properties = [];
return {
TAG: "Object",
loc: loc,
error: undefined,
properties: (match$2.forEach(function (child) {
var match = child.type;
var match$1 = child.children;
if (match !== "property") {
return ;
}
if (match$1 === undefined) {
return ;
}
if (match$1.length !== 2) {
return ;
}
var match$2 = match$1[0];
if (match$2.type !== "string") {
return ;
}
var name = match$2.value;
if (name === undefined) {
return ;
}
var rawValue = match$1[1];
var match$3 = transformNode(rawValue, ctx);
if (!Array.isArray(name) && (name === null || typeof name !== "object") && typeof name !== "number" && typeof name !== "string" && typeof name !== "boolean" || !(typeof name === "string" && match$3 !== undefined)) {
return ;
} else {
properties.push({
loc: rangeFromNode(child, ctx.lineLookup),
name: name,
value: match$3
});
return ;
}
}), properties)
};
}
var dummyPos$1 = {
start: {
line: 0,
column: 0
},
end_: {
line: 0,
column: 0
}
};
function withoutQueryParams(path) {
return getOr(path.split("?")[0], "");
}
function decodePathParams(path, loc, lineNum, ctx, parentContext) {
var pathWithoutQueryParams = withoutQueryParams(path);
var foundPathParams = [];
var currentContext;
var startCharIdx = loc.start.column + 1 | 0;
var addParamIfNotAlreadyPresent = function (currentCtx, paramLoc) {
var alreadySeenPathParam = find(parentContext.seenPathParams, (function (param) {
var match = param.seenAtPosition;
return match.text.text === currentCtx.paramName;
}));
if (alreadySeenPathParam !== undefined) {
if (alreadySeenPathParam.seenInSourceFile === ctx.routeFileName) {
return ctx.addDecodeError(paramLoc, "Path parameter \"" + currentCtx.paramName + "\" already exists in route \"" + alreadySeenPathParam.seenInSourceFile + "\". Path parameters cannot appear more than once per full path.");
} else {
return ctx.addDecodeError(paramLoc, "Path parameter \"" + currentCtx.paramName + "\" already exists in file \"" + alreadySeenPathParam.seenInSourceFile + "\" (route with name \"" + alreadySeenPathParam.seenInSourceFile + "\"), which is a parent to this route. Path names need to be unique per full route, including parents/children.");
}
}
var textNode_loc = {
start: {
line: lineNum,
column: currentCtx.startChar
},
end_: {
line: lineNum,
column: path.length - 1 | 0
}
};
var textNode_text = currentCtx.paramName;
var textNode = {
loc: textNode_loc,
text: textNode_text
};
foundPathParams.push(currentCtx.matchBranches.length > 0 ? ({
TAG: "PathParamWithMatchBranches",
text: textNode,
matchArms: currentCtx.matchBranches
}) : (
currentCtx.currentParamCustomType.length > 0 ? ({
TAG: "PathParam",
text: textNode,
pathToCustomModuleWithTypeT: currentCtx.currentParamCustomType
}) : ({
TAG: "PathParam",
text: textNode
})
));
};
for(var charIdx = 0 ,charIdx_finish = pathWithoutQueryParams.length; charIdx < charIdx_finish; ++charIdx){
var charLoc_start = {
line: lineNum,
column: startCharIdx + charIdx | 0
};
var charLoc_end_ = {
line: lineNum,
column: (startCharIdx + charIdx | 0) + 1 | 0
};
var charLoc = {
start: charLoc_start,
end_: charLoc_end_
};
var match = currentContext;
var match$1 = getOr(pathWithoutQueryParams[charIdx], "");
if (match !== undefined) {
if (match$1 === "/") {
if (match.paramName.length === 0) {
ctx.addDecodeError({
start: {
line: lineNum,
column: (startCharIdx + charIdx | 0) - 1 | 0
},
end_: {
line: lineNum,
column: startCharIdx + charIdx | 0
}
}, "Path parameter names cannot be empty.");
}
var paramLoc_start = {
line: lineNum,
column: match.startChar
};
var paramLoc_end_ = {
line: lineNum,
column: (startCharIdx + charIdx | 0) - 1 | 0
};
var paramLoc = {
start: paramLoc_start,
end_: paramLoc_end_
};
addParamIfNotAlreadyPresent(match, paramLoc);
currentContext = undefined;
} else {
switch (match.inContext) {
case "ParamName" :
var exit = 0;
switch (match$1) {
case "(" :
currentContext = {
startChar: match.startChar,
endChar: match.endChar,
paramName: match.paramName,
inContext: "MatchBranches",
currentParamCustomType: match.currentParamCustomType,
currentMatchParam: match.currentMatchParam,
matchBranches: match.matchBranches
};
break;
case ":" :
if (match.paramName.length > 0) {
currentContext = {
startChar: match.startChar,
endChar: match.endChar,
paramName: match.paramName,
inContext: "ParamCustomType",
currentParamCustomType: match.currentParamCustomType,
currentMatchParam: match.currentMatchParam,
matchBranches: match.matchBranches
};
} else {
exit = 1;
}
break;
default:
exit = 1;
}
if (exit === 1) {
currentContext = {
startChar: match.startChar,
endChar: match.endChar,
paramName: match.paramName + match$1,
inContext: match.inContext,
currentParamCustomType: match.currentParamCustomType,
currentMatchParam: match.currentMatchParam,
matchBranches: match.matchBranches
};
var match$2 = match.paramName.length;
if (match$2 !== 0) {
if (/[A-Za-z0-9_]/.test(match$1)) ; else {
ctx.addDecodeError(charLoc, "\"" + match$1 + "\" is not a valid character in a path parameter. Path parameters can contain letters, digits, dots and underscores.");
}
} else if (/[a-z]/.test(match$1)) ; else {
ctx.addDecodeError(charLoc, "Path parameters must start with a lowercase letter.");
}
}
break;
case "ParamCustomType" :
currentContext = {
startChar: match.startChar,
endChar: match.endChar,
paramName: match.paramName,
inContext: match.inContext,
currentParamCustomType: match.currentParamCustomType + match$1,
currentMatchParam: match.currentMatchParam,
matchBranches: match.matchBranches
};
var match$3 = match.paramName.length;
if (match$3 !== 0) {
if (/[A-Za-z0-9_\.]/.test(match$1)) ; else {
ctx.addDecodeError(charLoc, "\"" + match$1 + "\" is not a valid character in a path parameter. Path parameters can contain letters, digits, dots and underscores.");
}
} else if (/[A-Z]/.test(match$1)) ; else {
ctx.addDecodeError(charLoc, "Path parameter type references must refer to a module, and therefore must start with an uppercase letter.");
}
break;
case "MatchBranches" :
var exit$1 = 0;
switch (match$1) {
case ")" :
case "|" :
exit$1 = 1;
break;
default:
currentContext = {
startChar: match.startChar,
endChar: match.endChar,
paramName: match.paramName,
inContext: match.inContext,
currentParamCustomType: match.currentParamCustomType,
currentMatchParam: match.currentMatchParam + match$1,
matchBranches: match.matchBranches
};
var match$4 = match.currentMatchParam.length;
if (match$4 !== 0) {
if (/[A-Za-z0-9_\-]/.test(match$1)) ; else {
ctx.addDecodeError(charLoc, "\"" + match$1 + "\" is not a valid character in a path match branch. Path match branches can contain letters, digits, underscores and hyphens.");
}
} else if (/[a-zA-Z]/.test(match$1)) ; else {
ctx.addDecodeError(charLoc, "Path param match branches must start with a letter.");
}
}
if (exit$1 === 1) {
currentContext = {
startChar: match.startChar,
endChar: match.endChar,
paramName: match.paramName,
inContext: match.inContext,
currentParamCustomType: match.currentParamCustomType,
currentMatchParam: "",
matchBranches: match.matchBranches.concat([match.currentMatchParam])
};
}
break;
}
}
} else if (match$1 === ":") {
currentContext = {
startChar: startCharIdx + charIdx | 0,
endChar: undefined,
paramName: "",
inContext: "ParamName",
currentParamCustomType: "",
currentMatchParam: "",
matchBranches: []
};
}
}
var currentCtx = currentContext;
if (currentCtx !== undefined) {
var currentParamCustomType = currentCtx.currentParamCustomType;
if (currentParamCustomType.length > 0 && !currentParamCustomType.endsWith(".t")) {
ctx.addDecodeError({
start: {
line: lineNum,
column: currentCtx.startChar
},
end_: {
line: lineNum,
column: startCharIdx + pathWithoutQueryParams.length | 0
}
}, "Custom path parameters type annotations must refer to a type t in a module, hence end with \".t\".");
} else {
var paramLoc_start$1 = {
line: lineNum,
column: currentCtx.startChar
};
var paramLoc_end_$1 = {
line: lineNum,
column: startCharIdx + pathWithoutQueryParams.length | 0
};
var paramLoc$1 = {
start: paramLoc_start$1,
end_: paramLoc_end_$1
};
addParamIfNotAlreadyPresent(currentCtx, paramLoc$1);
}
}
return foundPathParams;
}
var queryParamNames = [
"string",
"int",
"float",
"bool",
"array"
];
function handleCompletedParam(completedParamCtx, ctx, foundQueryParams, lineNum) {
var keyEndChar = completedParamCtx.keyEndChar;
if (keyEndChar === undefined) {
return ;
}
var typeStartChar = completedParamCtx.typeStartChar;
if (typeStartChar === undefined) {
return ;
}
var typeEndChar = completedParamCtx.typeEndChar;
if (typeEndChar === undefined) {
return ;
}
var rawTypeText = completedParamCtx.rawTypeText;
if (rawTypeText === undefined) {
return ;
}
var queryParamLoc_start = {
line: lineNum,
column: typeStartChar
};
var queryParamLoc_end_ = {
line: lineNum,
column: typeEndChar
};
var queryParamLoc = {
start: queryParamLoc_start,
end_: queryParamLoc_end_
};
var keyLoc_start = {
line: lineNum,
column: completedParamCtx.keyStartChar
};
var keyLoc_end_ = {
line: lineNum,
column: keyEndChar
};
var keyLoc = {
start: keyLoc_start,
end_: keyLoc_end_
};
var queryParam = stringToQueryParam(rawTypeText);
if (queryParam.TAG === "Ok") {
foundQueryParams.push({
name: {
loc: keyLoc,
text: completedParamCtx.key
},
queryParam: [
queryParamLoc,
queryParam._0
]
});
return ;
}
var fuzzyMatches = FastFuzzy.search(rawTypeText, queryParamNames);
var message = "\"" + rawTypeText + "\" is not a valid query param type.\n " + (
fuzzyMatches.length > 0 ? "Did you mean \"" + fuzzyMatches[0] + "\"?" : (
rawTypeText === "boolean" ? "Did you mean \"bool\"?" : "Valid types are: string, int, float, bool, custom modules (SomeModule.t), and arrays of those."
)
);
ctx.addDecodeError(queryParamLoc, message);
}
function decodeQueryParams(path, loc, lineNum, ctx, parentContext) {
var queryParamsStr = path.split("?").pop();
if (queryParamsStr === undefined) {
return [];
}
var startChar = ((loc.start.column + path.length | 0) - queryParamsStr.length | 0) + 1 | 0;
var foundQueryParams = [];
var context;
for(var charIdx = 0 ,charIdx_finish = queryParamsStr.length; charIdx < charIdx_finish; ++charIdx){
var $$char = getOr(queryParamsStr[charIdx], "");
var match = context;
if (match !== undefined) {
var rawTypeText = match.rawTypeText;
switch ($$char) {
case "&" :
handleCompletedParam({
keyStartChar: match.keyStartChar,
keyEndChar: match.keyEndChar,
typeStartChar: match.typeStartChar,
typeEndChar: startChar + charIdx | 0,
key: match.key,
rawTypeText: match.rawTypeText
}, ctx, foundQueryParams, lineNum);
context = undefined;
break;
case "=" :
context = {
keyStartChar: match.keyStartChar,
keyEndChar: (startChar + charIdx | 0) - 1 | 0,
typeStartChar: (startChar + charIdx | 0) + 1 | 0,
typeEndChar: match.typeEndChar,
key: match.key,
rawTypeText: ""
};
break;
default:
context = rawTypeText !== undefined ? ({
keyStartChar: match.keyStartChar,
keyEndChar: match.keyEndChar,
typeStartChar: match.typeStartChar,
typeEndChar: match.typeEndChar,
key: match.key,
rawTypeText: rawTypeText + $$char
}) : ({
keyStartChar: match.keyStartChar,
keyEndChar: match.keyEndChar,
typeStartChar: match.typeStartChar,
typeEndChar: match.typeEndChar,
key: match.key + $$char,
rawTypeText: match.rawTypeText
});
}
} else {
context = {
keyStartChar: startChar + charIdx | 0,
keyEndChar: undefined,
typeStartChar: undefined,
typeEndChar: undefined,
key: $$char,
rawTypeText: undefined
};
}
}
var completedParamCtx = context;
if (completedParamCtx !== undefined) {
handleCompletedParam({
keyStartChar: completedParamCtx.keyStartChar,
keyEndChar: completedParamCtx.keyEndChar,
typeStartChar: completedParamCtx.typeStartChar,
typeEndChar: loc.end_.column - 1 | 0,
key: completedParamCtx.key,
rawTypeText: completedParamCtx.rawTypeText
}, ctx, foundQueryParams, lineNum);
}
var queryParamsResult = [];
parentContext.seenQueryParams.concat(foundQueryParams).forEach(function (param) {
if (queryParamsResult.some(function (p) {
return p.name.text === param.name.text;
})) {
return ;
} else {
queryParamsResult.push(param);
return ;
}
});
return queryParamsResult;
}
function routeWithNameAlreadyExists(existingChildren, routeName) {
return existingChildren.some(function (child) {
if (child.TAG === "Include") {
return routeWithNameAlreadyExists(child._0.content, routeName);
} else {
return RouteName.getRouteName(child._0.name) === routeName;
}
});
}
function validateName(nameNode, ctx, siblings) {
if (nameNode === undefined) {
return ;
}
var node = nameNode.value;
var loc = nameNode.loc;
if (node.TAG === "String") {
var value = node.value;
var loc$1 = node.loc;
if (value === "Route") {
ctx.addDecodeError(loc$1, "\"Route\" is a reserved name. Please change your route name to something else.");
return {
loc: loc$1,
name: "_"
};
}
var match = /^[A-Z][a-zA-Z0-9_]+$/.test(value);
var match$1 = routeWithNameAlreadyExists(siblings, value);
if (match) {
if (match$1) {
ctx.addDecodeError(loc$1, "Duplicate route \"" + value + "\". Routes cannot have siblings with the same names.");
return {
loc: loc$1,
name: "_"
};
} else {
return {
loc: loc$1,
name: value
};
}
} else {
ctx.addDecodeError(loc$1, "\"" + value + "\" is not a valid route name. Route names need to start with an uppercase letter, and can only contain letters, digits and underscores.");
return {
loc: loc$1,
name: "_"
};
}
}
ctx.addDecodeError(loc, "\"name\" needs to be a string. Found " + nodeToString(node) + ".");
return {
loc: loc,
name: "_"
};
}
function validatePath(pathNode, ctx, parentContext) {
if (pathNode === undefined) {
return ;
}
v