|  | /** | 
|  | * @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; | 
|  | } | 
|  | } |