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