blob: 4163d267a8c79723dec9b35f4e370c83c704617f [file] [log] [blame]
/**
* @license
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import * as fs from "fs";
import * as path from "path";
import {FileUtils} from "../utils/file-utils";
import {
Redirect,
isRedirectToNodeModule,
isRedirectToDir,
RedirectToNodeModule,
PathRedirect
} from "./redirects";
export class HtmlFileUtils {
public static getPathRelativeToRoot(parentHtml: string, fileHref: string): string {
if (fileHref.startsWith('/')) {
return fileHref.substring(1);
}
return path.join(path.dirname(parentHtml), fileHref);
}
public static getImportPathRelativeToParent(rootDir: string, parentFile: string, importPath: string) {
if (importPath.startsWith('/')) {
importPath = importPath.substr(1);
}
const parentDir = path.dirname(
path.resolve(path.join(rootDir, parentFile)));
const fullImportPath = path.resolve(path.join(rootDir, importPath));
const relativePath = path.relative(parentDir, fullImportPath);
return relativePath.startsWith('../') ?
relativePath : "./" + relativePath;
}
}
interface RedirectForFile {
to: PathRedirect;
pathToFile: string;
}
interface ResolvedPath {
target: string;
insideNodeModules: boolean;
}
/** RedirectsResolver based on the list of redirects, calculates
* new import path
*/
export class RedirectsResolver {
public constructor(private readonly redirects: Redirect[]) {
}
/** resolve returns new path instead of pathRelativeToRoot; */
public resolve(pathRelativeToRoot: string, resolveNodeModules: boolean): ResolvedPath {
const redirect = this.findRedirect(pathRelativeToRoot);
if (!redirect) {
return {target: pathRelativeToRoot, insideNodeModules: false};
}
if (isRedirectToNodeModule(redirect.to)) {
return {
target: resolveNodeModules ? RedirectsResolver.resolveNodeModuleFile(redirect.to,
redirect.pathToFile) : pathRelativeToRoot,
insideNodeModules: resolveNodeModules
};
}
if (isRedirectToDir(redirect.to)) {
let newDir = redirect.to.dir;
if (!newDir.endsWith('/')) {
newDir = newDir + '/';
}
return {target: `${newDir}${redirect.pathToFile}`, insideNodeModules: false}
}
throw new Error(`Invalid redirect for path: ${pathRelativeToRoot}`);
}
private static resolveNodeModuleFile(npmRedirect: RedirectToNodeModule, pathToFile: string): string {
if(npmRedirect.files && npmRedirect.files[pathToFile]) {
pathToFile = npmRedirect.files[pathToFile];
}
return `${npmRedirect.npm_module}/${pathToFile}`;
}
private findRedirect(relativePathToRoot: string): RedirectForFile | undefined {
if(!relativePathToRoot.startsWith('/')) {
relativePathToRoot = '/' + relativePathToRoot;
}
for(const redirect of this.redirects) {
const normalizedFrom = redirect.from + (redirect.from.endsWith('/') ? '' : '/');
if(relativePathToRoot.startsWith(normalizedFrom)) {
return {
to: redirect.to,
pathToFile: relativePathToRoot.substring(normalizedFrom.length)
};
}
}
return undefined;
}
}