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